Skip to content

Commit 562222b

Browse files
authored
Rollup merge of #144999 - Zalathar:remove-mcdc, r=oli-obk
coverage: Remove all unstable support for MC/DC instrumentation Preliminary support for a partial implementation of “Modified Condition/Decision Coverage” instrumentation was added behind the unstable flag `-Zcoverage-options=mcdc` in 2024. These are the most substantial PRs involved: - #123409 - #126733 At the time, I accepted these PRs with relatively modest scrutiny, because I did not want to stand in the way of independent work on MC/DC instrumentation. My hope was that ongoing work by interested contributors would lead to the code becoming clearer and more maintainable over time. --- However, that MC/DC code has proven itself to be a major burden on overall maintenance of coverage instrumentation, and a major obstacle to other planned improvements, such as internal changes needed for proper support of macro expansion regions. I have also become reluctant to accept any further MC/DC-related changes that would increase this burden. That tension has resulted in an unhappy impasse. On one hand, the present MC/DC implementation is not yet complete, and shows little sign of being complete at an acceptable level of code quality in the foreseeable future. On the other hand, the continued existence of this partial MC/DC implementation is imposing serious maintenance burdens on every other aspect of coverage instrumentation, and is preventing some of the very improvements that would make it easier to accept expanded MC/DC support in the future. While I know this will be disappointing to some, I think the healthy way forward is accept that I made the wrong call in accepting the current implementation, and to remove it entirely from the compiler.
2 parents 2edfd36 + 81ed042 commit 562222b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+30
-3046
lines changed

compiler/rustc_codegen_llvm/src/builder.rs

Lines changed: 0 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1886,48 +1886,4 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
18861886
) {
18871887
self.call_intrinsic("llvm.instrprof.increment", &[], &[fn_name, hash, num_counters, index]);
18881888
}
1889-
1890-
/// Emits a call to `llvm.instrprof.mcdc.parameters`.
1891-
///
1892-
/// This doesn't produce any code directly, but is used as input by
1893-
/// the LLVM pass that handles coverage instrumentation.
1894-
///
1895-
/// (See clang's [`CodeGenPGO::emitMCDCParameters`] for comparison.)
1896-
///
1897-
/// [`CodeGenPGO::emitMCDCParameters`]:
1898-
/// https://github.com/rust-lang/llvm-project/blob/5399a24/clang/lib/CodeGen/CodeGenPGO.cpp#L1124
1899-
#[instrument(level = "debug", skip(self))]
1900-
pub(crate) fn mcdc_parameters(
1901-
&mut self,
1902-
fn_name: &'ll Value,
1903-
hash: &'ll Value,
1904-
bitmap_bits: &'ll Value,
1905-
) {
1906-
self.call_intrinsic("llvm.instrprof.mcdc.parameters", &[], &[fn_name, hash, bitmap_bits]);
1907-
}
1908-
1909-
#[instrument(level = "debug", skip(self))]
1910-
pub(crate) fn mcdc_tvbitmap_update(
1911-
&mut self,
1912-
fn_name: &'ll Value,
1913-
hash: &'ll Value,
1914-
bitmap_index: &'ll Value,
1915-
mcdc_temp: &'ll Value,
1916-
) {
1917-
let args = &[fn_name, hash, bitmap_index, mcdc_temp];
1918-
self.call_intrinsic("llvm.instrprof.mcdc.tvbitmap.update", &[], args);
1919-
}
1920-
1921-
#[instrument(level = "debug", skip(self))]
1922-
pub(crate) fn mcdc_condbitmap_reset(&mut self, mcdc_temp: &'ll Value) {
1923-
self.store(self.const_i32(0), mcdc_temp, self.tcx.data_layout.i32_align.abi);
1924-
}
1925-
1926-
#[instrument(level = "debug", skip(self))]
1927-
pub(crate) fn mcdc_condbitmap_update(&mut self, cond_index: &'ll Value, mcdc_temp: &'ll Value) {
1928-
let align = self.tcx.data_layout.i32_align.abi;
1929-
let current_tv_index = self.load(self.cx.type_i32(), mcdc_temp, align);
1930-
let new_tv_index = self.add(current_tv_index, cond_index);
1931-
self.store(new_tv_index, mcdc_temp, align);
1932-
}
19331889
}

compiler/rustc_codegen_llvm/src/coverageinfo/ffi.rs

Lines changed: 3 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -73,48 +73,6 @@ pub(crate) struct CounterExpression {
7373
pub(crate) rhs: Counter,
7474
}
7575

76-
pub(crate) mod mcdc {
77-
use rustc_middle::mir::coverage::{ConditionId, ConditionInfo, DecisionInfo};
78-
79-
/// Must match the layout of `LLVMRustMCDCDecisionParameters`.
80-
#[repr(C)]
81-
#[derive(Clone, Copy, Debug, Default)]
82-
pub(crate) struct DecisionParameters {
83-
bitmap_idx: u32,
84-
num_conditions: u16,
85-
}
86-
87-
type LLVMConditionId = i16;
88-
89-
/// Must match the layout of `LLVMRustMCDCBranchParameters`.
90-
#[repr(C)]
91-
#[derive(Clone, Copy, Debug, Default)]
92-
pub(crate) struct BranchParameters {
93-
condition_id: LLVMConditionId,
94-
condition_ids: [LLVMConditionId; 2],
95-
}
96-
97-
impl From<ConditionInfo> for BranchParameters {
98-
fn from(value: ConditionInfo) -> Self {
99-
let to_llvm_cond_id = |cond_id: Option<ConditionId>| {
100-
cond_id.and_then(|id| LLVMConditionId::try_from(id.as_usize()).ok()).unwrap_or(-1)
101-
};
102-
let ConditionInfo { condition_id, true_next_id, false_next_id } = value;
103-
Self {
104-
condition_id: to_llvm_cond_id(Some(condition_id)),
105-
condition_ids: [to_llvm_cond_id(false_next_id), to_llvm_cond_id(true_next_id)],
106-
}
107-
}
108-
}
109-
110-
impl From<DecisionInfo> for DecisionParameters {
111-
fn from(info: DecisionInfo) -> Self {
112-
let DecisionInfo { bitmap_idx, num_conditions } = info;
113-
Self { bitmap_idx, num_conditions }
114-
}
115-
}
116-
}
117-
11876
/// A span of source code coordinates to be embedded in coverage metadata.
11977
///
12078
/// Must match the layout of `LLVMRustCoverageSpan`.
@@ -148,26 +106,14 @@ pub(crate) struct Regions {
148106
pub(crate) code_regions: Vec<CodeRegion>,
149107
pub(crate) expansion_regions: Vec<ExpansionRegion>,
150108
pub(crate) branch_regions: Vec<BranchRegion>,
151-
pub(crate) mcdc_branch_regions: Vec<MCDCBranchRegion>,
152-
pub(crate) mcdc_decision_regions: Vec<MCDCDecisionRegion>,
153109
}
154110

155111
impl Regions {
156112
/// Returns true if none of this structure's tables contain any regions.
157113
pub(crate) fn has_no_regions(&self) -> bool {
158-
let Self {
159-
code_regions,
160-
expansion_regions,
161-
branch_regions,
162-
mcdc_branch_regions,
163-
mcdc_decision_regions,
164-
} = self;
165-
166-
code_regions.is_empty()
167-
&& expansion_regions.is_empty()
168-
&& branch_regions.is_empty()
169-
&& mcdc_branch_regions.is_empty()
170-
&& mcdc_decision_regions.is_empty()
114+
let Self { code_regions, expansion_regions, branch_regions } = self;
115+
116+
code_regions.is_empty() && expansion_regions.is_empty() && branch_regions.is_empty()
171117
}
172118
}
173119

@@ -195,21 +141,3 @@ pub(crate) struct BranchRegion {
195141
pub(crate) true_counter: Counter,
196142
pub(crate) false_counter: Counter,
197143
}
198-
199-
/// Must match the layout of `LLVMRustCoverageMCDCBranchRegion`.
200-
#[derive(Clone, Debug)]
201-
#[repr(C)]
202-
pub(crate) struct MCDCBranchRegion {
203-
pub(crate) cov_span: CoverageSpan,
204-
pub(crate) true_counter: Counter,
205-
pub(crate) false_counter: Counter,
206-
pub(crate) mcdc_branch_params: mcdc::BranchParameters,
207-
}
208-
209-
/// Must match the layout of `LLVMRustCoverageMCDCDecisionRegion`.
210-
#[derive(Clone, Debug)]
211-
#[repr(C)]
212-
pub(crate) struct MCDCDecisionRegion {
213-
pub(crate) cov_span: CoverageSpan,
214-
pub(crate) mcdc_decision_params: mcdc::DecisionParameters,
215-
}

compiler/rustc_codegen_llvm/src/coverageinfo/llvm_cov.rs

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,7 @@ pub(crate) fn write_function_mappings_to_buffer(
6363
expressions: &[ffi::CounterExpression],
6464
regions: &ffi::Regions,
6565
) -> Vec<u8> {
66-
let ffi::Regions {
67-
code_regions,
68-
expansion_regions,
69-
branch_regions,
70-
mcdc_branch_regions,
71-
mcdc_decision_regions,
72-
} = regions;
66+
let ffi::Regions { code_regions, expansion_regions, branch_regions } = regions;
7367

7468
// SAFETY:
7569
// - All types are FFI-compatible and have matching representations in Rust/C++.
@@ -87,10 +81,6 @@ pub(crate) fn write_function_mappings_to_buffer(
8781
expansion_regions.len(),
8882
branch_regions.as_ptr(),
8983
branch_regions.len(),
90-
mcdc_branch_regions.as_ptr(),
91-
mcdc_branch_regions.len(),
92-
mcdc_decision_regions.as_ptr(),
93-
mcdc_decision_regions.len(),
9484
buffer,
9585
)
9686
})

compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,6 @@ fn fill_region_tables<'tcx>(
140140
code_regions,
141141
expansion_regions: _, // FIXME(Zalathar): Fill out support for expansion regions
142142
branch_regions,
143-
mcdc_branch_regions,
144-
mcdc_decision_regions,
145143
} = &mut covfun.regions;
146144

147145
// For each counter/region pair in this function+file, convert it to a
@@ -161,20 +159,6 @@ fn fill_region_tables<'tcx>(
161159
false_counter: counter_for_bcb(false_bcb),
162160
});
163161
}
164-
MappingKind::MCDCBranch { true_bcb, false_bcb, mcdc_params } => {
165-
mcdc_branch_regions.push(ffi::MCDCBranchRegion {
166-
cov_span,
167-
true_counter: counter_for_bcb(true_bcb),
168-
false_counter: counter_for_bcb(false_bcb),
169-
mcdc_branch_params: ffi::mcdc::BranchParameters::from(mcdc_params),
170-
});
171-
}
172-
MappingKind::MCDCDecision(mcdc_decision_params) => {
173-
mcdc_decision_regions.push(ffi::MCDCDecisionRegion {
174-
cov_span,
175-
mcdc_decision_params: ffi::mcdc::DecisionParameters::from(mcdc_decision_params),
176-
});
177-
}
178162
}
179163
}
180164
}

compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs

Lines changed: 4 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
use std::cell::{OnceCell, RefCell};
22
use std::ffi::{CStr, CString};
33

4-
use rustc_abi::Size;
54
use rustc_codegen_ssa::traits::{
6-
BuilderMethods, ConstCodegenMethods, CoverageInfoBuilderMethods, MiscCodegenMethods,
5+
ConstCodegenMethods, CoverageInfoBuilderMethods, MiscCodegenMethods,
76
};
8-
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
7+
use rustc_data_structures::fx::FxIndexMap;
98
use rustc_middle::mir::coverage::CoverageKind;
109
use rustc_middle::ty::Instance;
1110
use tracing::{debug, instrument};
@@ -28,34 +27,13 @@ pub(crate) struct CguCoverageContext<'ll, 'tcx> {
2827
/// symbol name, and `llvm-cov` will exit fatally if it can't resolve that
2928
/// hash back to an entry in the binary's `__llvm_prf_names` linker section.
3029
pub(crate) pgo_func_name_var_map: RefCell<FxIndexMap<Instance<'tcx>, &'ll llvm::Value>>,
31-
pub(crate) mcdc_condition_bitmap_map: RefCell<FxHashMap<Instance<'tcx>, Vec<&'ll llvm::Value>>>,
3230

3331
covfun_section_name: OnceCell<CString>,
3432
}
3533

3634
impl<'ll, 'tcx> CguCoverageContext<'ll, 'tcx> {
3735
pub(crate) fn new() -> Self {
38-
Self {
39-
pgo_func_name_var_map: Default::default(),
40-
mcdc_condition_bitmap_map: Default::default(),
41-
covfun_section_name: Default::default(),
42-
}
43-
}
44-
45-
/// LLVM use a temp value to record evaluated mcdc test vector of each decision, which is
46-
/// called condition bitmap. In order to handle nested decisions, several condition bitmaps can
47-
/// be allocated for a function body. These values are named `mcdc.addr.{i}` and are a 32-bit
48-
/// integers. They respectively hold the condition bitmaps for decisions with a depth of `i`.
49-
fn try_get_mcdc_condition_bitmap(
50-
&self,
51-
instance: &Instance<'tcx>,
52-
decision_depth: u16,
53-
) -> Option<&'ll llvm::Value> {
54-
self.mcdc_condition_bitmap_map
55-
.borrow()
56-
.get(instance)
57-
.and_then(|bitmap_map| bitmap_map.get(decision_depth as usize))
58-
.copied() // Dereference Option<&&Value> to Option<&Value>
36+
Self { pgo_func_name_var_map: Default::default(), covfun_section_name: Default::default() }
5937
}
6038

6139
/// Returns the list of instances considered "used" in this CGU, as
@@ -105,38 +83,6 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
10583
}
10684

10785
impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
108-
fn init_coverage(&mut self, instance: Instance<'tcx>) {
109-
let Some(function_coverage_info) =
110-
self.tcx.instance_mir(instance.def).function_coverage_info.as_deref()
111-
else {
112-
return;
113-
};
114-
115-
// If there are no MC/DC bitmaps to set up, return immediately.
116-
if function_coverage_info.mcdc_bitmap_bits == 0 {
117-
return;
118-
}
119-
120-
let fn_name = self.ensure_pgo_func_name_var(instance);
121-
let hash = self.const_u64(function_coverage_info.function_source_hash);
122-
let bitmap_bits = self.const_u32(function_coverage_info.mcdc_bitmap_bits as u32);
123-
self.mcdc_parameters(fn_name, hash, bitmap_bits);
124-
125-
// Create pointers named `mcdc.addr.{i}` to stack-allocated condition bitmaps.
126-
let mut cond_bitmaps = vec![];
127-
for i in 0..function_coverage_info.mcdc_num_condition_bitmaps {
128-
// MC/DC intrinsics will perform loads/stores that use the ABI default
129-
// alignment for i32, so our variable declaration should match.
130-
let align = self.tcx.data_layout.i32_align.abi;
131-
let cond_bitmap = self.alloca(Size::from_bytes(4), align);
132-
llvm::set_value_name(cond_bitmap, format!("mcdc.addr.{i}").as_bytes());
133-
self.store(self.const_i32(0), cond_bitmap, align);
134-
cond_bitmaps.push(cond_bitmap);
135-
}
136-
137-
self.coverage_cx().mcdc_condition_bitmap_map.borrow_mut().insert(instance, cond_bitmaps);
138-
}
139-
14086
#[instrument(level = "debug", skip(self))]
14187
fn add_coverage(&mut self, instance: Instance<'tcx>, kind: &CoverageKind) {
14288
// Our caller should have already taken care of inlining subtleties,
@@ -153,7 +99,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
15399
// When that happens, we currently just discard those statements, so
154100
// the corresponding code will be undercounted.
155101
// FIXME(Zalathar): Find a better solution for mixed-coverage builds.
156-
let Some(coverage_cx) = &bx.cx.coverage_cx else { return };
102+
let Some(_coverage_cx) = &bx.cx.coverage_cx else { return };
157103

158104
let Some(function_coverage_info) =
159105
bx.tcx.instance_mir(instance.def).function_coverage_info.as_deref()
@@ -185,30 +131,6 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
185131
}
186132
// If a BCB doesn't have an associated physical counter, there's nothing to codegen.
187133
CoverageKind::VirtualCounter { .. } => {}
188-
CoverageKind::CondBitmapUpdate { index, decision_depth } => {
189-
let cond_bitmap = coverage_cx
190-
.try_get_mcdc_condition_bitmap(&instance, decision_depth)
191-
.expect("mcdc cond bitmap should have been allocated for updating");
192-
let cond_index = bx.const_i32(index as i32);
193-
bx.mcdc_condbitmap_update(cond_index, cond_bitmap);
194-
}
195-
CoverageKind::TestVectorBitmapUpdate { bitmap_idx, decision_depth } => {
196-
let cond_bitmap =
197-
coverage_cx.try_get_mcdc_condition_bitmap(&instance, decision_depth).expect(
198-
"mcdc cond bitmap should have been allocated for merging \
199-
into the global bitmap",
200-
);
201-
assert!(
202-
bitmap_idx as usize <= function_coverage_info.mcdc_bitmap_bits,
203-
"bitmap index of the decision out of range"
204-
);
205-
206-
let fn_name = bx.ensure_pgo_func_name_var(instance);
207-
let hash = bx.const_u64(function_coverage_info.function_source_hash);
208-
let bitmap_index = bx.const_u32(bitmap_idx);
209-
bx.mcdc_tvbitmap_update(fn_name, hash, bitmap_index, cond_bitmap);
210-
bx.mcdc_condbitmap_reset(cond_bitmap);
211-
}
212134
}
213135
}
214136
}

compiler/rustc_codegen_llvm/src/llvm/ffi.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2056,10 +2056,6 @@ unsafe extern "C" {
20562056
NumExpansionRegions: size_t,
20572057
BranchRegions: *const crate::coverageinfo::ffi::BranchRegion,
20582058
NumBranchRegions: size_t,
2059-
MCDCBranchRegions: *const crate::coverageinfo::ffi::MCDCBranchRegion,
2060-
NumMCDCBranchRegions: size_t,
2061-
MCDCDecisionRegions: *const crate::coverageinfo::ffi::MCDCDecisionRegion,
2062-
NumMCDCDecisionRegions: size_t,
20632059
BufferOut: &RustString,
20642060
);
20652061

compiler/rustc_codegen_ssa/src/mir/mod.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -296,10 +296,6 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
296296
// Apply debuginfo to the newly allocated locals.
297297
fx.debug_introduce_locals(&mut start_bx, consts_debug_info.unwrap_or_default());
298298

299-
// If the backend supports coverage, and coverage is enabled for this function,
300-
// do any necessary start-of-function codegen (e.g. locals for MC/DC bitmaps).
301-
start_bx.init_coverage(instance);
302-
303299
// The builders will be created separately for each basic block at `codegen_block`.
304300
// So drop the builder of `start_llbb` to avoid having two at the same time.
305301
drop(start_bx);

compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,6 @@ use rustc_middle::mir::coverage::CoverageKind;
22
use rustc_middle::ty::Instance;
33

44
pub trait CoverageInfoBuilderMethods<'tcx> {
5-
/// Performs any start-of-function codegen needed for coverage instrumentation.
6-
///
7-
/// Can be a no-op in backends that don't support coverage instrumentation.
8-
fn init_coverage(&mut self, _instance: Instance<'tcx>) {}
9-
105
/// Handle the MIR coverage info in a backend-specific way.
116
///
127
/// This can potentially be a no-op in backends that don't support

compiler/rustc_interface/src/tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -777,7 +777,7 @@ fn test_unstable_options_tracking_hash() {
777777
tracked!(
778778
coverage_options,
779779
CoverageOptions {
780-
level: CoverageLevel::Mcdc,
780+
level: CoverageLevel::Branch,
781781
// (don't collapse test-only options onto the same line)
782782
discard_all_spans_in_codegen: true,
783783
}

0 commit comments

Comments
 (0)