Rollup merge of #125700 - Zalathar:limit-overflow, r=nnethercote

coverage: Avoid overflow when the MC/DC condition limit is exceeded

Fix for the test failure seen in https://github.com/rust-lang/rust/pull/124571#issuecomment-2099620869.

If we perform this subtraction first, it can sometimes overflow to -1 before the addition can bring its value back to 0.

That behaviour seems to be benign, but it nevertheless causes test failures in compiler configurations that check for overflow.

``@rustbot`` label +A-code-coverage
This commit is contained in:
Matthias Krüger 2024-05-29 20:12:33 +02:00 committed by GitHub
commit 9a61146765
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 313 additions and 2 deletions

View File

@ -217,12 +217,13 @@ pub fn visit_evaluated_condition(
} }
_ => { _ => {
// Do not generate mcdc mappings and statements for decisions with too many conditions. // Do not generate mcdc mappings and statements for decisions with too many conditions.
let rebase_idx = self.branch_spans.len() - decision.conditions_num + 1; // Therefore, first erase the condition info of the (N-1) previous branch spans.
let rebase_idx = self.branch_spans.len() - (decision.conditions_num - 1);
for branch in &mut self.branch_spans[rebase_idx..] { for branch in &mut self.branch_spans[rebase_idx..] {
branch.condition_info = None; branch.condition_info = None;
} }
// ConditionInfo of this branch shall also be reset. // Then, erase this last branch span's info too, for a total of N.
condition_info = None; condition_info = None;
tcx.dcx().emit_warn(MCDCExceedsConditionNumLimit { tcx.dcx().emit_warn(MCDCExceedsConditionNumLimit {

View File

@ -0,0 +1,162 @@
Function name: condition_limit::bad
Raw bytes (204): 0x[01, 01, 2c, 01, 05, 05, 1d, 05, 1d, 7a, 19, 05, 1d, 7a, 19, 05, 1d, 76, 15, 7a, 19, 05, 1d, 76, 15, 7a, 19, 05, 1d, 72, 11, 76, 15, 7a, 19, 05, 1d, 72, 11, 76, 15, 7a, 19, 05, 1d, 6e, 0d, 72, 11, 76, 15, 7a, 19, 05, 1d, 6e, 0d, 72, 11, 76, 15, 7a, 19, 05, 1d, 9f, 01, 02, a3, 01, 1d, a7, 01, 19, ab, 01, 15, af, 01, 11, 09, 0d, 21, 9b, 01, 9f, 01, 02, a3, 01, 1d, a7, 01, 19, ab, 01, 15, af, 01, 11, 09, 0d, 11, 01, 14, 01, 03, 09, 20, 05, 02, 03, 08, 00, 09, 05, 00, 0d, 00, 0e, 20, 7a, 1d, 00, 0d, 00, 0e, 7a, 00, 12, 00, 13, 20, 76, 19, 00, 12, 00, 13, 76, 00, 17, 00, 18, 20, 72, 15, 00, 17, 00, 18, 72, 00, 1c, 00, 1d, 20, 6e, 11, 00, 1c, 00, 1d, 6e, 00, 21, 00, 22, 20, 6a, 0d, 00, 21, 00, 22, 6a, 00, 26, 00, 27, 20, 21, 09, 00, 26, 00, 27, 21, 00, 28, 02, 06, 9b, 01, 02, 06, 00, 07, 97, 01, 01, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 44
- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
- expression 1 operands: lhs = Counter(1), rhs = Counter(7)
- expression 2 operands: lhs = Counter(1), rhs = Counter(7)
- expression 3 operands: lhs = Expression(30, Sub), rhs = Counter(6)
- expression 4 operands: lhs = Counter(1), rhs = Counter(7)
- expression 5 operands: lhs = Expression(30, Sub), rhs = Counter(6)
- expression 6 operands: lhs = Counter(1), rhs = Counter(7)
- expression 7 operands: lhs = Expression(29, Sub), rhs = Counter(5)
- expression 8 operands: lhs = Expression(30, Sub), rhs = Counter(6)
- expression 9 operands: lhs = Counter(1), rhs = Counter(7)
- expression 10 operands: lhs = Expression(29, Sub), rhs = Counter(5)
- expression 11 operands: lhs = Expression(30, Sub), rhs = Counter(6)
- expression 12 operands: lhs = Counter(1), rhs = Counter(7)
- expression 13 operands: lhs = Expression(28, Sub), rhs = Counter(4)
- expression 14 operands: lhs = Expression(29, Sub), rhs = Counter(5)
- expression 15 operands: lhs = Expression(30, Sub), rhs = Counter(6)
- expression 16 operands: lhs = Counter(1), rhs = Counter(7)
- expression 17 operands: lhs = Expression(28, Sub), rhs = Counter(4)
- expression 18 operands: lhs = Expression(29, Sub), rhs = Counter(5)
- expression 19 operands: lhs = Expression(30, Sub), rhs = Counter(6)
- expression 20 operands: lhs = Counter(1), rhs = Counter(7)
- expression 21 operands: lhs = Expression(27, Sub), rhs = Counter(3)
- expression 22 operands: lhs = Expression(28, Sub), rhs = Counter(4)
- expression 23 operands: lhs = Expression(29, Sub), rhs = Counter(5)
- expression 24 operands: lhs = Expression(30, Sub), rhs = Counter(6)
- expression 25 operands: lhs = Counter(1), rhs = Counter(7)
- expression 26 operands: lhs = Expression(27, Sub), rhs = Counter(3)
- expression 27 operands: lhs = Expression(28, Sub), rhs = Counter(4)
- expression 28 operands: lhs = Expression(29, Sub), rhs = Counter(5)
- expression 29 operands: lhs = Expression(30, Sub), rhs = Counter(6)
- expression 30 operands: lhs = Counter(1), rhs = Counter(7)
- expression 31 operands: lhs = Expression(39, Add), rhs = Expression(0, Sub)
- expression 32 operands: lhs = Expression(40, Add), rhs = Counter(7)
- expression 33 operands: lhs = Expression(41, Add), rhs = Counter(6)
- expression 34 operands: lhs = Expression(42, Add), rhs = Counter(5)
- expression 35 operands: lhs = Expression(43, Add), rhs = Counter(4)
- expression 36 operands: lhs = Counter(2), rhs = Counter(3)
- expression 37 operands: lhs = Counter(8), rhs = Expression(38, Add)
- expression 38 operands: lhs = Expression(39, Add), rhs = Expression(0, Sub)
- expression 39 operands: lhs = Expression(40, Add), rhs = Counter(7)
- expression 40 operands: lhs = Expression(41, Add), rhs = Counter(6)
- expression 41 operands: lhs = Expression(42, Add), rhs = Counter(5)
- expression 42 operands: lhs = Expression(43, Add), rhs = Counter(4)
- expression 43 operands: lhs = Counter(2), rhs = Counter(3)
Number of file 0 mappings: 17
- Code(Counter(0)) at (prev + 20, 1) to (start + 3, 9)
- Branch { true: Counter(1), false: Expression(0, Sub) } at (prev + 3, 8) to (start + 0, 9)
true = c1
false = (c0 - c1)
- Code(Counter(1)) at (prev + 0, 13) to (start + 0, 14)
- Branch { true: Expression(30, Sub), false: Counter(7) } at (prev + 0, 13) to (start + 0, 14)
true = (c1 - c7)
false = c7
- Code(Expression(30, Sub)) at (prev + 0, 18) to (start + 0, 19)
= (c1 - c7)
- Branch { true: Expression(29, Sub), false: Counter(6) } at (prev + 0, 18) to (start + 0, 19)
true = ((c1 - c7) - c6)
false = c6
- Code(Expression(29, Sub)) at (prev + 0, 23) to (start + 0, 24)
= ((c1 - c7) - c6)
- Branch { true: Expression(28, Sub), false: Counter(5) } at (prev + 0, 23) to (start + 0, 24)
true = (((c1 - c7) - c6) - c5)
false = c5
- Code(Expression(28, Sub)) at (prev + 0, 28) to (start + 0, 29)
= (((c1 - c7) - c6) - c5)
- Branch { true: Expression(27, Sub), false: Counter(4) } at (prev + 0, 28) to (start + 0, 29)
true = ((((c1 - c7) - c6) - c5) - c4)
false = c4
- Code(Expression(27, Sub)) at (prev + 0, 33) to (start + 0, 34)
= ((((c1 - c7) - c6) - c5) - c4)
- Branch { true: Expression(26, Sub), false: Counter(3) } at (prev + 0, 33) to (start + 0, 34)
true = (((((c1 - c7) - c6) - c5) - c4) - c3)
false = c3
- Code(Expression(26, Sub)) at (prev + 0, 38) to (start + 0, 39)
= (((((c1 - c7) - c6) - c5) - c4) - c3)
- Branch { true: Counter(8), false: Counter(2) } at (prev + 0, 38) to (start + 0, 39)
true = c8
false = c2
- Code(Counter(8)) at (prev + 0, 40) to (start + 2, 6)
- Code(Expression(38, Add)) at (prev + 2, 6) to (start + 0, 7)
= ((((((c2 + c3) + c4) + c5) + c6) + c7) + (c0 - c1))
- Code(Expression(37, Add)) at (prev + 1, 1) to (start + 0, 2)
= (c8 + ((((((c2 + c3) + c4) + c5) + c6) + c7) + (c0 - c1)))
Function name: condition_limit::good
Raw bytes (180): 0x[01, 01, 20, 01, 05, 05, 19, 05, 19, 52, 15, 05, 19, 52, 15, 05, 19, 4e, 11, 52, 15, 05, 19, 4e, 11, 52, 15, 05, 19, 4a, 0d, 4e, 11, 52, 15, 05, 19, 4a, 0d, 4e, 11, 52, 15, 05, 19, 73, 02, 77, 19, 7b, 15, 7f, 11, 09, 0d, 1d, 6f, 73, 02, 77, 19, 7b, 15, 7f, 11, 09, 0d, 10, 01, 0c, 01, 03, 09, 28, 00, 06, 03, 08, 00, 22, 30, 05, 02, 01, 06, 00, 00, 08, 00, 09, 05, 00, 0d, 00, 0e, 30, 52, 19, 06, 05, 00, 00, 0d, 00, 0e, 52, 00, 12, 00, 13, 30, 4e, 15, 05, 04, 00, 00, 12, 00, 13, 4e, 00, 17, 00, 18, 30, 4a, 11, 04, 03, 00, 00, 17, 00, 18, 4a, 00, 1c, 00, 1d, 30, 46, 0d, 03, 02, 00, 00, 1c, 00, 1d, 46, 00, 21, 00, 22, 30, 1d, 09, 02, 00, 00, 00, 21, 00, 22, 1d, 00, 23, 02, 06, 6f, 02, 06, 00, 07, 6b, 01, 01, 00, 02]
Number of files: 1
- file 0 => global file 1
Number of expressions: 32
- expression 0 operands: lhs = Counter(0), rhs = Counter(1)
- expression 1 operands: lhs = Counter(1), rhs = Counter(6)
- expression 2 operands: lhs = Counter(1), rhs = Counter(6)
- expression 3 operands: lhs = Expression(20, Sub), rhs = Counter(5)
- expression 4 operands: lhs = Counter(1), rhs = Counter(6)
- expression 5 operands: lhs = Expression(20, Sub), rhs = Counter(5)
- expression 6 operands: lhs = Counter(1), rhs = Counter(6)
- expression 7 operands: lhs = Expression(19, Sub), rhs = Counter(4)
- expression 8 operands: lhs = Expression(20, Sub), rhs = Counter(5)
- expression 9 operands: lhs = Counter(1), rhs = Counter(6)
- expression 10 operands: lhs = Expression(19, Sub), rhs = Counter(4)
- expression 11 operands: lhs = Expression(20, Sub), rhs = Counter(5)
- expression 12 operands: lhs = Counter(1), rhs = Counter(6)
- expression 13 operands: lhs = Expression(18, Sub), rhs = Counter(3)
- expression 14 operands: lhs = Expression(19, Sub), rhs = Counter(4)
- expression 15 operands: lhs = Expression(20, Sub), rhs = Counter(5)
- expression 16 operands: lhs = Counter(1), rhs = Counter(6)
- expression 17 operands: lhs = Expression(18, Sub), rhs = Counter(3)
- expression 18 operands: lhs = Expression(19, Sub), rhs = Counter(4)
- expression 19 operands: lhs = Expression(20, Sub), rhs = Counter(5)
- expression 20 operands: lhs = Counter(1), rhs = Counter(6)
- expression 21 operands: lhs = Expression(28, Add), rhs = Expression(0, Sub)
- expression 22 operands: lhs = Expression(29, Add), rhs = Counter(6)
- expression 23 operands: lhs = Expression(30, Add), rhs = Counter(5)
- expression 24 operands: lhs = Expression(31, Add), rhs = Counter(4)
- expression 25 operands: lhs = Counter(2), rhs = Counter(3)
- expression 26 operands: lhs = Counter(7), rhs = Expression(27, Add)
- expression 27 operands: lhs = Expression(28, Add), rhs = Expression(0, Sub)
- expression 28 operands: lhs = Expression(29, Add), rhs = Counter(6)
- expression 29 operands: lhs = Expression(30, Add), rhs = Counter(5)
- expression 30 operands: lhs = Expression(31, Add), rhs = Counter(4)
- expression 31 operands: lhs = Counter(2), rhs = Counter(3)
Number of file 0 mappings: 16
- Code(Counter(0)) at (prev + 12, 1) to (start + 3, 9)
- MCDCDecision { bitmap_idx: 0, conditions_num: 6 } at (prev + 3, 8) to (start + 0, 34)
- MCDCBranch { true: Counter(1), false: Expression(0, Sub), condition_id: 1, true_next_id: 6, false_next_id: 0 } at (prev + 0, 8) to (start + 0, 9)
true = c1
false = (c0 - c1)
- Code(Counter(1)) at (prev + 0, 13) to (start + 0, 14)
- MCDCBranch { true: Expression(20, Sub), false: Counter(6), condition_id: 6, true_next_id: 5, false_next_id: 0 } at (prev + 0, 13) to (start + 0, 14)
true = (c1 - c6)
false = c6
- Code(Expression(20, Sub)) at (prev + 0, 18) to (start + 0, 19)
= (c1 - c6)
- MCDCBranch { true: Expression(19, Sub), false: Counter(5), condition_id: 5, true_next_id: 4, false_next_id: 0 } at (prev + 0, 18) to (start + 0, 19)
true = ((c1 - c6) - c5)
false = c5
- Code(Expression(19, Sub)) at (prev + 0, 23) to (start + 0, 24)
= ((c1 - c6) - c5)
- MCDCBranch { true: Expression(18, Sub), false: Counter(4), condition_id: 4, true_next_id: 3, false_next_id: 0 } at (prev + 0, 23) to (start + 0, 24)
true = (((c1 - c6) - c5) - c4)
false = c4
- Code(Expression(18, Sub)) at (prev + 0, 28) to (start + 0, 29)
= (((c1 - c6) - c5) - c4)
- MCDCBranch { true: Expression(17, Sub), false: Counter(3), condition_id: 3, true_next_id: 2, false_next_id: 0 } at (prev + 0, 28) to (start + 0, 29)
true = ((((c1 - c6) - c5) - c4) - c3)
false = c3
- Code(Expression(17, Sub)) at (prev + 0, 33) to (start + 0, 34)
= ((((c1 - c6) - c5) - c4) - c3)
- MCDCBranch { true: Counter(7), false: Counter(2), condition_id: 2, true_next_id: 0, false_next_id: 0 } at (prev + 0, 33) to (start + 0, 34)
true = c7
false = c2
- Code(Counter(7)) at (prev + 0, 35) to (start + 2, 6)
- Code(Expression(27, Add)) at (prev + 2, 6) to (start + 0, 7)
= (((((c2 + c3) + c4) + c5) + c6) + (c0 - c1))
- Code(Expression(26, Add)) at (prev + 1, 1) to (start + 0, 2)
= (c7 + (((((c2 + c3) + c4) + c5) + c6) + (c0 - c1)))

View File

@ -0,0 +1,76 @@
LL| |#![feature(coverage_attribute)]
LL| |//@ edition: 2021
LL| |//@ min-llvm-version: 18
LL| |//@ compile-flags: -Zcoverage-options=mcdc
LL| |//@ llvm-cov-flags: --show-branches=count --show-mcdc
LL| |
LL| |// Check that MC/DC instrumentation can gracefully handle conditions that
LL| |// exceed LLVM's limit of 6 conditions per decision.
LL| |//
LL| |// (The limit is enforced in `compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs`.)
LL| |
LL| 1|fn good() {
LL| 1| // With only 6 conditions, perform full MC/DC instrumentation.
LL| 1| let [a, b, c, d, e, f] = <[bool; 6]>::default();
LL| 1| if a && b && c && d && e && f {
^0 ^0 ^0 ^0 ^0
------------------
| Branch (LL:8): [True: 0, False: 1]
| Branch (LL:13): [True: 0, False: 0]
| Branch (LL:18): [True: 0, False: 0]
| Branch (LL:23): [True: 0, False: 0]
| Branch (LL:28): [True: 0, False: 0]
| Branch (LL:33): [True: 0, False: 0]
------------------
|---> MC/DC Decision Region (LL:8) to (LL:34)
|
| Number of Conditions: 6
| Condition C1 --> (LL:8)
| Condition C2 --> (LL:13)
| Condition C3 --> (LL:18)
| Condition C4 --> (LL:23)
| Condition C5 --> (LL:28)
| Condition C6 --> (LL:33)
|
| Executed MC/DC Test Vectors:
|
| C1, C2, C3, C4, C5, C6 Result
| 1 { F, -, -, -, -, - = F }
|
| C1-Pair: not covered
| C2-Pair: not covered
| C3-Pair: not covered
| C4-Pair: not covered
| C5-Pair: not covered
| C6-Pair: not covered
| MC/DC Coverage for Decision: 0.00%
|
------------------
LL| 0| core::hint::black_box("hello");
LL| 1| }
LL| 1|}
LL| |
LL| 1|fn bad() {
LL| 1| // With 7 conditions, fall back to branch instrumentation only.
LL| 1| let [a, b, c, d, e, f, g] = <[bool; 7]>::default();
LL| 1| if a && b && c && d && e && f && g {
^0 ^0 ^0 ^0 ^0 ^0
------------------
| Branch (LL:8): [True: 0, False: 1]
| Branch (LL:13): [True: 0, False: 0]
| Branch (LL:18): [True: 0, False: 0]
| Branch (LL:23): [True: 0, False: 0]
| Branch (LL:28): [True: 0, False: 0]
| Branch (LL:33): [True: 0, False: 0]
| Branch (LL:38): [True: 0, False: 0]
------------------
LL| 0| core::hint::black_box("hello");
LL| 1| }
LL| 1|}
LL| |
LL| |#[coverage(off)]
LL| |fn main() {
LL| | good();
LL| | bad();
LL| |}

View File

@ -0,0 +1,32 @@
#![feature(coverage_attribute)]
//@ edition: 2021
//@ min-llvm-version: 18
//@ compile-flags: -Zcoverage-options=mcdc
//@ llvm-cov-flags: --show-branches=count --show-mcdc
// Check that MC/DC instrumentation can gracefully handle conditions that
// exceed LLVM's limit of 6 conditions per decision.
//
// (The limit is enforced in `compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs`.)
fn good() {
// With only 6 conditions, perform full MC/DC instrumentation.
let [a, b, c, d, e, f] = <[bool; 6]>::default();
if a && b && c && d && e && f {
core::hint::black_box("hello");
}
}
fn bad() {
// With 7 conditions, fall back to branch instrumentation only.
let [a, b, c, d, e, f, g] = <[bool; 7]>::default();
if a && b && c && d && e && f && g {
core::hint::black_box("hello");
}
}
#[coverage(off)]
fn main() {
good();
bad();
}

View File

@ -0,0 +1,8 @@
warning: Conditions number of the decision (7) exceeds limit (6). MCDC analysis will not count this expression.
--> $DIR/mcdc-condition-limit.rs:29:8
|
LL | if a && b && c && d && e && f && g {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
warning: 1 warning emitted

View File

@ -0,0 +1,32 @@
//@ edition: 2021
//@ min-llvm-version: 18
//@ revisions: good bad
//@ check-pass
//@ compile-flags: -Cinstrument-coverage -Zcoverage-options=mcdc -Zno-profiler-runtime
// Check that we emit some kind of diagnostic when MC/DC instrumentation sees
// code that exceeds the limit of 6 conditions per decision, and falls back
// to only instrumenting that code for branch coverage.
//
// See also `tests/coverage/mcdc/condition-limit.rs`, which tests the actual
// effect on instrumentation.
//
// (The limit is enforced in `compiler/rustc_mir_build/src/build/coverageinfo/mcdc.rs`.)
#[cfg(good)]
fn main() {
// 6 conditions is OK, so no diagnostic.
let [a, b, c, d, e, f] = <[bool; 6]>::default();
if a && b && c && d && e && f {
core::hint::black_box("hello");
}
}
#[cfg(bad)]
fn main() {
// 7 conditions is too many, so issue a diagnostic.
let [a, b, c, d, e, f, g] = <[bool; 7]>::default();
if a && b && c && d && e && f && g { //[bad]~ WARNING Conditions number of the decision
core::hint::black_box("hello");
}
}