Skip to content

Commit b268271

Browse files
author
zhuyunxing
committed
coverage. Add limit options for mcdc
1 parent 458c1fc commit b268271

File tree

4 files changed

+85
-29
lines changed

4 files changed

+85
-29
lines changed

compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs

+32-27
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,6 @@ use rustc_span::Span;
1515
use crate::build::Builder;
1616
use crate::errors::{MCDCExceedsConditionLimit, MCDCExceedsDecisionDepth};
1717

18-
/// The MCDC bitmap scales exponentially (2^n) based on the number of conditions seen,
19-
/// So llvm sets a maximum value prevents the bitmap footprint from growing too large without the user's knowledge.
20-
/// This limit may be relaxed if the [upstream change](https://github.com/llvm/llvm-project/pull/82448) is merged.
21-
const MAX_CONDITIONS_IN_DECISION: usize = 6;
22-
2318
/// MCDC allocates an i32 variable on stack for each depth. Ignore decisions nested too much to prevent it
2419
/// consuming excessive memory.
2520
const MAX_DECISION_DEPTH: u16 = 0x3FFF;
@@ -356,6 +351,7 @@ pub(crate) struct MCDCInfoBuilder {
356351
mcdc_targets: FxIndexMap<DecisionId, MCDCTargetInfo>,
357352
state: MCDCState,
358353
decision_id_gen: DecisionIdGen,
354+
required_num_test_vectors: usize,
359355
}
360356

361357
impl MCDCInfoBuilder {
@@ -365,6 +361,7 @@ impl MCDCInfoBuilder {
365361
mcdc_targets: FxIndexMap::default(),
366362
state: MCDCState::new(),
367363
decision_id_gen: DecisionIdGen::default(),
364+
required_num_test_vectors: 0,
368365
}
369366
}
370367

@@ -396,27 +393,30 @@ impl MCDCInfoBuilder {
396393
conditions: Vec<MCDCBranchSpan>,
397394
) -> Option<&mut MCDCTargetInfo> {
398395
let num_conditions = conditions.len();
399-
match num_conditions {
400-
0 => {
401-
unreachable!("Decision with no condition is not expected");
402-
}
403-
// Ignore decisions with only one condition given that mcdc for them is completely equivalent to branch coverage.
404-
2..=MAX_CONDITIONS_IN_DECISION => {
405-
let info = MCDCTargetInfo::new(decision, conditions);
406-
Some(self.mcdc_targets.entry(id).or_insert(info))
407-
}
408-
_ => {
409-
self.append_normal_branches(conditions);
410-
if num_conditions > MAX_CONDITIONS_IN_DECISION {
411-
tcx.dcx().emit_warn(MCDCExceedsConditionLimit {
412-
span: decision.span,
413-
num_conditions,
414-
max_conditions: MAX_CONDITIONS_IN_DECISION,
415-
});
416-
}
417-
None
418-
}
396+
let max_conditions = tcx.sess.coverage_mcdc_max_conditions_per_decision();
397+
let max_test_vectors = tcx.sess.coverage_mcdc_max_test_vectors();
398+
// Ignore decisions with only one condition given that mcdc for them is completely equivalent to branch coverage.
399+
if num_conditions <= 1 {
400+
return None;
401+
}
402+
if num_conditions > max_conditions {
403+
self.append_normal_branches(conditions);
404+
tcx.dcx().emit_warn(MCDCExceedsConditionLimit {
405+
span: decision.span,
406+
num_conditions,
407+
max_conditions,
408+
});
409+
return None;
419410
}
411+
412+
let info = MCDCTargetInfo::new(decision, conditions);
413+
let expected_num_tv = info.decision.num_test_vectors + self.required_num_test_vectors;
414+
if expected_num_tv > max_test_vectors {
415+
self.append_normal_branches(info.conditions);
416+
return None;
417+
}
418+
419+
Some(self.mcdc_targets.entry(id).or_insert(info))
420420
}
421421

422422
fn normalize_depth_from(&mut self, tcx: TyCtxt<'_>, id: DecisionId) {
@@ -499,8 +499,13 @@ impl MCDCInfoBuilder {
499499
pub(crate) fn into_done(
500500
self,
501501
) -> (Vec<MCDCBranchSpan>, Vec<(MCDCDecisionSpan, Vec<MCDCBranchSpan>)>) {
502-
let MCDCInfoBuilder { normal_branch_spans, mcdc_targets, state: _, decision_id_gen: _ } =
503-
self;
502+
let MCDCInfoBuilder {
503+
normal_branch_spans,
504+
mcdc_targets,
505+
state: _,
506+
decision_id_gen: _,
507+
required_num_test_vectors: _,
508+
} = self;
504509

505510
let mcdc_spans = mcdc_targets
506511
.into_values()

compiler/rustc_session/src/config.rs

+28-1
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ pub enum InstrumentCoverage {
146146
}
147147

148148
/// Individual flag values controlled by `-Z coverage-options`.
149-
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
149+
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
150150
pub struct CoverageOptions {
151151
pub level: CoverageLevel,
152152

@@ -157,6 +157,33 @@ pub struct CoverageOptions {
157157
/// For internal debugging only. If other code changes would make it hard
158158
/// to keep supporting this flag, remove it.
159159
pub no_mir_spans: bool,
160+
161+
/// Limit for number of conditions in a decision. By default the limit is
162+
/// `32767` and user defined limit shall not be larger than it.
163+
pub mcdc_max_conditions: usize,
164+
165+
/// Limit for number of test vectors of all decisions in a function.
166+
/// Each test vectors takes up 1 bit memory. By default the limit is
167+
/// `2,147,483,646` and user defined limit shall not be larger than it.
168+
pub mcdc_max_test_vectors: usize,
169+
}
170+
171+
impl Default for CoverageOptions {
172+
fn default() -> Self {
173+
Self {
174+
level: CoverageLevel::default(),
175+
no_mir_spans: false,
176+
mcdc_max_conditions: Self::MCDC_MAX_CONDITIONS_IN_DECISION,
177+
mcdc_max_test_vectors: Self::MCDC_MAX_TEST_VECTORS,
178+
}
179+
}
180+
}
181+
182+
impl CoverageOptions {
183+
// Maxium number of conditions in a decision specified by LLVM.
184+
pub const MCDC_MAX_CONDITIONS_IN_DECISION: usize = 0x7fff;
185+
// Maxium number of test vectors in a function specified by LLVM.
186+
pub const MCDC_MAX_TEST_VECTORS: usize = 0x7fffffff;
160187
}
161188

162189
/// Controls whether branch coverage or MC/DC coverage is enabled.

compiler/rustc_session/src/options.rs

+17-1
Original file line numberDiff line numberDiff line change
@@ -965,7 +965,23 @@ mod parse {
965965
"condition" => slot.level = CoverageLevel::Condition,
966966
"mcdc" => slot.level = CoverageLevel::Mcdc,
967967
"no-mir-spans" => slot.no_mir_spans = true,
968-
_ => return false,
968+
_ => {
969+
if let Some(max_conditions) = option
970+
.strip_prefix("mcdc-max-conditions=")
971+
.and_then(|num| num.parse::<usize>().ok())
972+
{
973+
slot.mcdc_max_conditions =
974+
max_conditions.min(CoverageOptions::MCDC_MAX_CONDITIONS_IN_DECISION);
975+
} else if let Some(max_test_vectors) = option
976+
.strip_prefix("mcdc-test-vectors=")
977+
.and_then(|num| num.parse::<usize>().ok())
978+
{
979+
slot.mcdc_max_test_vectors =
980+
max_test_vectors.min(CoverageOptions::MCDC_MAX_TEST_VECTORS);
981+
} else {
982+
return false;
983+
}
984+
}
969985
}
970986
}
971987
true

compiler/rustc_session/src/session.rs

+8
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,14 @@ impl Session {
368368
self.opts.unstable_opts.coverage_options.no_mir_spans
369369
}
370370

371+
pub fn coverage_mcdc_max_conditions_per_decision(&self) -> usize {
372+
self.opts.unstable_opts.coverage_options.mcdc_max_conditions
373+
}
374+
375+
pub fn coverage_mcdc_max_test_vectors(&self) -> usize {
376+
self.opts.unstable_opts.coverage_options.mcdc_max_test_vectors
377+
}
378+
371379
pub fn is_sanitizer_cfi_enabled(&self) -> bool {
372380
self.opts.unstable_opts.sanitizer.contains(SanitizerSet::CFI)
373381
}

0 commit comments

Comments
 (0)