|
1 |
| -use rustc_macros::Diagnostic; |
2 | 1 | use rustc_middle::{
|
3 |
| - mir::{ |
4 |
| - self, |
5 |
| - coverage::{CoverageKind, DecisionSpan}, |
6 |
| - Statement, |
7 |
| - }, |
| 2 | + mir::{self, coverage::CoverageKind, Statement}, |
8 | 3 | ty::TyCtxt,
|
9 | 4 | };
|
10 |
| -use rustc_span::Span; |
11 |
| - |
12 |
| -#[derive(Diagnostic)] |
13 |
| -#[diag(mir_transform_mcdc_too_many_conditions)] |
14 |
| -pub(crate) struct MCDCTooManyConditions { |
15 |
| - #[primary_span] |
16 |
| - pub span: Span, |
17 |
| - pub num_conditions: u32, |
18 |
| - pub max_conditions: u32, |
19 |
| -} |
| 5 | + |
| 6 | +use crate::coverage::graph::CoverageGraph; |
| 7 | +use crate::errors::MCDCTooManyConditions; |
20 | 8 |
|
21 | 9 | const MAX_COND_DECISION: u32 = 6;
|
22 | 10 |
|
| 11 | + |
23 | 12 | /// If MCDC coverage is enabled, add MCDC instrumentation to the function.
|
| 13 | +/// |
| 14 | +/// Assume a decision to be the following: |
| 15 | +/// |
| 16 | +/// if (A && B) || C { then(); } else { otherwise(); } |
| 17 | +/// |
| 18 | +/// The corresponding BDD (Binary Decision Diagram) will look like so: |
| 19 | +/// |
| 20 | +/// ``` |
| 21 | +/// │ |
| 22 | +/// ┌▼┐ |
| 23 | +/// │A│ |
| 24 | +/// ┌──┴─┴──┐ |
| 25 | +/// │t f│ |
| 26 | +/// │ │ |
| 27 | +/// ┌▼┐ ┌▼┐ |
| 28 | +/// │B├─────►C├───┐ |
| 29 | +/// └┬┘ f └┬┘ f│ |
| 30 | +/// │t │t │ |
| 31 | +/// ┌──▼───┐ │ ┌─▼─────────┐ |
| 32 | +/// │then()◄───┘ │otherwise()│ |
| 33 | +/// └──────┘ └───────────┘ |
| 34 | +/// ``` |
| 35 | +/// |
| 36 | +/// The coverage graph is similar, up to unwinding mechanics. The goal is to |
| 37 | +/// instrument each edge of the BDD to update two bitmaps. |
| 38 | +/// |
| 39 | +/// The first bitmap (CBM) is updated upon the evaluation of each contidion. |
| 40 | +/// It tracks the state of a condition at a given instant. |
| 41 | +/// is given an index, such that when the decision is taken, CBM represents the |
| 42 | +/// state of all conditions that were evaluated (1 for true, 0 for |
| 43 | +/// false/skipped). |
| 44 | +/// |
| 45 | +/// The second bitmap (TVBM) is the test vector bitmap. It tracks all the test |
| 46 | +/// vectors that were executed during the program's life. It is updated before |
| 47 | +/// branching to `then()` or `otherwise()` by using CBM as an integer n and |
| 48 | +/// setting the nth integer of TVBM to 1. |
| 49 | +/// Basically, we do `TVBM |= 1 << CBM`. |
| 50 | +/// |
| 51 | +/// Note: This technique is very sub-optimal, as it implies that TVBM to have a |
| 52 | +/// size of 2^n bits, (n being the number of conditions in the decision) to |
| 53 | +/// account for every combination, even though only a subset of theses |
| 54 | +/// combinations are actually achievable because of logical operators |
| 55 | +/// short-circuit semantics. |
| 56 | +/// An improvement to this instrumentation is being implemented in Clang and |
| 57 | +/// shall be ported to Rustc in the future: |
| 58 | +/// https://discourse.llvm.org/t/rfc-coverage-new-algorithm-and-file-format-for-mc-dc/76798 |
| 59 | +/// |
| 60 | +/// In the meantime, to follow the original implementation of Clang, we use this |
| 61 | +/// suboptimal technique, while limiting the size of the decisions we can |
| 62 | +/// instrument to 6 conditions. |
| 63 | +/// |
24 | 64 | /// Will do the following things :
|
25 | 65 | /// 1. Add an instruction in the first basic block for the codegen to call
|
26 | 66 | /// the `instrprof.mcdc.parameters` instrinsic.
|
27 |
| -pub fn instrument_function_mcdc<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir::Body<'tcx>) { |
| 67 | +/// 2. Before each decision, add an instruction to reset CBM. |
| 68 | +/// 3. Add an isntruction to update CBM upon condition evaluation. |
| 69 | +/// 4. Add an instruction to update TVBM with the CBM value before jumping |
| 70 | +/// to the `then` or `else` block. |
| 71 | +/// 5. Build mappings for the reporting tools to get the results and transpose |
| 72 | +/// it to the source code. |
| 73 | +pub fn instrument_function_mcdc<'tcx>( |
| 74 | + tcx: TyCtxt<'tcx>, |
| 75 | + mir_body: &mut mir::Body<'tcx>, |
| 76 | + coverage_graph: &CoverageGraph, |
| 77 | +) { |
| 78 | + let _ = coverage_graph; |
28 | 79 | if !tcx.sess.instrument_coverage_mcdc() {
|
29 | 80 | return;
|
30 | 81 | }
|
| 82 | + debug!("Called instrument_function_mcdc()"); |
31 | 83 |
|
32 | 84 | let Some(branch_info) = mir_body.coverage_branch_info.as_deref() else {
|
33 | 85 | return;
|
34 | 86 | };
|
35 | 87 |
|
36 |
| - // Compute the total sum of needed bytes for the current body. |
37 |
| - let needed_bytes: u32 = branch_info |
38 |
| - .decision_spans |
39 |
| - .iter() |
40 |
| - .filter_map(|DecisionSpan { span, num_conditions }| { |
41 |
| - if *num_conditions > MAX_COND_DECISION { |
42 |
| - tcx.dcx().emit_warn(MCDCTooManyConditions { |
43 |
| - span: *span, |
44 |
| - num_conditions: *num_conditions, |
45 |
| - max_conditions: MAX_COND_DECISION, |
46 |
| - }); |
47 |
| - None |
48 |
| - } else { |
49 |
| - Some(1 << num_conditions) |
50 |
| - } |
51 |
| - }) |
52 |
| - .sum(); |
| 88 | + let mut needed_bytes = 0; |
| 89 | + let mut bitmap_indexes = vec![]; |
| 90 | + |
| 91 | + for dec_span in branch_info.decision_spans.iter() { |
| 92 | + // Skip uninstrumentable conditions. |
| 93 | + if dec_span.num_conditions > MAX_COND_DECISION { |
| 94 | + tcx.dcx().emit_warn(MCDCTooManyConditions { |
| 95 | + span: dec_span.span, |
| 96 | + num_conditions: dec_span.num_conditions, |
| 97 | + max_conditions: MAX_COND_DECISION, |
| 98 | + }); |
| 99 | + continue; |
| 100 | + } |
| 101 | + bitmap_indexes.push(needed_bytes); |
| 102 | + needed_bytes += 1 << dec_span.num_conditions; |
| 103 | + } |
53 | 104 |
|
| 105 | + if needed_bytes == 0 { |
| 106 | + // No decision to instrument |
| 107 | + return; |
| 108 | + } |
| 109 | + |
| 110 | + // In the first BB, specify that we need to allocate bitmaps. |
54 | 111 | let entry_bb = &mut mir_body.basic_blocks_mut()[mir::START_BLOCK];
|
55 | 112 | let mcdc_parameters_statement = Statement {
|
56 | 113 | source_info: entry_bb.terminator().source_info,
|
57 | 114 | kind: mir::StatementKind::Coverage(CoverageKind::MCDCBitmapRequire { needed_bytes }),
|
58 | 115 | };
|
59 | 116 | entry_bb.statements.insert(0, mcdc_parameters_statement);
|
| 117 | + |
| 118 | + // For each decision: |
| 119 | + // - Find its 'root' basic block |
| 120 | + // - Insert a 'reset CDM' instruction |
| 121 | + // - for each branch, find the root condition, give it an index and |
| 122 | + // call a condbitmapUpdate on it |
| 123 | + // - Get the Output markers, and insert goto blocks before to put a |
| 124 | + // tvbitmapupdate on it. |
60 | 125 | }
|
0 commit comments