Skip to content

Commit 613bccc

Browse files
authored
Rollup merge of rust-lang#124555 - Zalathar:init-coverage, r=nnethercote
coverage: Clean up creation of MC/DC condition bitmaps This PR improves the code for creating and initializing [MC/DC](https://en.wikipedia.org/wiki/Modified_condition/decision_coverage) condition bitmap variables, as introduced by rust-lang#123409 and modified by rust-lang#124255. - The condition bitmap variables are now created eagerly at the start of per-function codegen, via a new `init_coverage` method in `CoverageInfoBuilderMethods`. This avoids having to retroactively create the bitmaps while doing codegen for an individual coverage statement. - As a result, we can now create and initialize those bitmaps using existing safe APIs, instead of having to perform our own unsafe call to `llvm::LLVMBuildAlloca`. - This PR also tweaks the way we count the number of condition bitmaps needed, by tracking the total number of bitmaps needed (max depth + 1), instead of only tracking the maximum depth. This reduces the potential for subtle off-by-one confusion.
2 parents bd305e1 + de972b7 commit 613bccc

File tree

8 files changed

+72
-56
lines changed

8 files changed

+72
-56
lines changed

compiler/rustc_abi/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ use rustc_macros::{Decodable_Generic, Encodable_Generic};
2121
use std::iter::Step;
2222

2323
mod layout;
24+
#[cfg(test)]
25+
mod tests;
2426

2527
pub use layout::LayoutCalculator;
2628

compiler/rustc_abi/src/tests.rs

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
use super::*;
2+
3+
#[test]
4+
fn align_constants() {
5+
assert_eq!(Align::ONE, Align::from_bytes(1).unwrap());
6+
assert_eq!(Align::EIGHT, Align::from_bytes(8).unwrap());
7+
}

compiler/rustc_codegen_llvm/src/builder.rs

+12-24
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use rustc_data_structures::small_c_str::SmallCStr;
1717
use rustc_hir::def_id::DefId;
1818
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs;
1919
use rustc_middle::ty::layout::{
20-
FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasTyCtxt, LayoutError, LayoutOfHelpers, TyAndLayout,
20+
FnAbiError, FnAbiOfHelpers, FnAbiRequest, LayoutError, LayoutOfHelpers, TyAndLayout,
2121
};
2222
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
2323
use rustc_sanitizers::{cfi, kcfi};
@@ -27,7 +27,6 @@ use rustc_target::abi::{self, call::FnAbi, Align, Size, WrappingRange};
2727
use rustc_target::spec::{HasTargetSpec, SanitizerSet, Target};
2828
use smallvec::SmallVec;
2929
use std::borrow::Cow;
30-
use std::ffi::CString;
3130
use std::iter;
3231
use std::ops::Deref;
3332
use std::ptr;
@@ -1705,13 +1704,21 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
17051704
kcfi_bundle
17061705
}
17071706

1707+
/// Emits a call to `llvm.instrprof.mcdc.parameters`.
1708+
///
1709+
/// This doesn't produce any code directly, but is used as input by
1710+
/// the LLVM pass that handles coverage instrumentation.
1711+
///
1712+
/// (See clang's [`CodeGenPGO::emitMCDCParameters`] for comparison.)
1713+
///
1714+
/// [`CodeGenPGO::emitMCDCParameters`]:
1715+
/// https://github.com/rust-lang/llvm-project/blob/5399a24/clang/lib/CodeGen/CodeGenPGO.cpp#L1124
17081716
pub(crate) fn mcdc_parameters(
17091717
&mut self,
17101718
fn_name: &'ll Value,
17111719
hash: &'ll Value,
17121720
bitmap_bytes: &'ll Value,
1713-
max_decision_depth: u32,
1714-
) -> Vec<&'ll Value> {
1721+
) {
17151722
debug!("mcdc_parameters() with args ({:?}, {:?}, {:?})", fn_name, hash, bitmap_bytes);
17161723

17171724
assert!(llvm_util::get_version() >= (18, 0, 0), "MCDC intrinsics require LLVM 18 or later");
@@ -1724,8 +1731,6 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
17241731
let args = &[fn_name, hash, bitmap_bytes];
17251732
let args = self.check_call("call", llty, llfn, args);
17261733

1727-
let mut cond_bitmaps = vec![];
1728-
17291734
unsafe {
17301735
let _ = llvm::LLVMRustBuildCall(
17311736
self.llbuilder,
@@ -1736,23 +1741,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
17361741
[].as_ptr(),
17371742
0 as c_uint,
17381743
);
1739-
// Create condition bitmap named `mcdc.addr`.
1740-
for i in 0..=max_decision_depth {
1741-
let mut bx = Builder::with_cx(self.cx);
1742-
bx.position_at_start(llvm::LLVMGetFirstBasicBlock(self.llfn()));
1743-
1744-
let name = CString::new(format!("mcdc.addr.{i}")).unwrap();
1745-
let cond_bitmap = {
1746-
let alloca =
1747-
llvm::LLVMBuildAlloca(bx.llbuilder, bx.cx.type_i32(), name.as_ptr());
1748-
llvm::LLVMSetAlignment(alloca, 4);
1749-
alloca
1750-
};
1751-
bx.store(self.const_i32(0), cond_bitmap, self.tcx().data_layout.i32_align.abi);
1752-
cond_bitmaps.push(cond_bitmap);
1753-
}
17541744
}
1755-
cond_bitmaps
17561745
}
17571746

17581747
pub(crate) fn mcdc_tvbitmap_update(
@@ -1794,8 +1783,7 @@ impl<'a, 'll, 'tcx> Builder<'a, 'll, 'tcx> {
17941783
0 as c_uint,
17951784
);
17961785
}
1797-
let i32_align = self.tcx().data_layout.i32_align.abi;
1798-
self.store(self.const_i32(0), mcdc_temp, i32_align);
1786+
self.store(self.const_i32(0), mcdc_temp, self.tcx.data_layout.i32_align.abi);
17991787
}
18001788

18011789
pub(crate) fn mcdc_condbitmap_update(

compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs

+38-28
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ use rustc_codegen_ssa::traits::{
1313
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
1414
use rustc_llvm::RustString;
1515
use rustc_middle::bug;
16-
use rustc_middle::mir::coverage::{CoverageKind, FunctionCoverageInfo};
16+
use rustc_middle::mir::coverage::CoverageKind;
1717
use rustc_middle::ty::layout::HasTyCtxt;
1818
use rustc_middle::ty::Instance;
19-
use rustc_target::abi::Align;
19+
use rustc_target::abi::{Align, Size};
2020

2121
use std::cell::RefCell;
2222

@@ -91,6 +91,42 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
9191
}
9292

9393
impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
94+
fn init_coverage(&mut self, instance: Instance<'tcx>) {
95+
let Some(function_coverage_info) =
96+
self.tcx.instance_mir(instance.def).function_coverage_info.as_deref()
97+
else {
98+
return;
99+
};
100+
101+
// If there are no MC/DC bitmaps to set up, return immediately.
102+
if function_coverage_info.mcdc_bitmap_bytes == 0 {
103+
return;
104+
}
105+
106+
let fn_name = self.get_pgo_func_name_var(instance);
107+
let hash = self.const_u64(function_coverage_info.function_source_hash);
108+
let bitmap_bytes = self.const_u32(function_coverage_info.mcdc_bitmap_bytes);
109+
self.mcdc_parameters(fn_name, hash, bitmap_bytes);
110+
111+
// Create pointers named `mcdc.addr.{i}` to stack-allocated condition bitmaps.
112+
let mut cond_bitmaps = vec![];
113+
for i in 0..function_coverage_info.mcdc_num_condition_bitmaps {
114+
// MC/DC intrinsics will perform loads/stores that use the ABI default
115+
// alignment for i32, so our variable declaration should match.
116+
let align = self.tcx.data_layout.i32_align.abi;
117+
let cond_bitmap = self.alloca(Size::from_bytes(4), align);
118+
llvm::set_value_name(cond_bitmap, format!("mcdc.addr.{i}").as_bytes());
119+
self.store(self.const_i32(0), cond_bitmap, align);
120+
cond_bitmaps.push(cond_bitmap);
121+
}
122+
123+
self.coverage_context()
124+
.expect("always present when coverage is enabled")
125+
.mcdc_condition_bitmap_map
126+
.borrow_mut()
127+
.insert(instance, cond_bitmaps);
128+
}
129+
94130
#[instrument(level = "debug", skip(self))]
95131
fn add_coverage(&mut self, instance: Instance<'tcx>, kind: &CoverageKind) {
96132
// Our caller should have already taken care of inlining subtleties,
@@ -109,10 +145,6 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
109145
return;
110146
};
111147

112-
if function_coverage_info.mcdc_bitmap_bytes > 0 {
113-
ensure_mcdc_parameters(bx, instance, function_coverage_info);
114-
}
115-
116148
let Some(coverage_context) = bx.coverage_context() else { return };
117149
let mut coverage_map = coverage_context.function_coverage_map.borrow_mut();
118150
let func_coverage = coverage_map
@@ -193,28 +225,6 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
193225
}
194226
}
195227

196-
fn ensure_mcdc_parameters<'ll, 'tcx>(
197-
bx: &mut Builder<'_, 'll, 'tcx>,
198-
instance: Instance<'tcx>,
199-
function_coverage_info: &FunctionCoverageInfo,
200-
) {
201-
let Some(cx) = bx.coverage_context() else { return };
202-
if cx.mcdc_condition_bitmap_map.borrow().contains_key(&instance) {
203-
return;
204-
}
205-
206-
let fn_name = bx.get_pgo_func_name_var(instance);
207-
let hash = bx.const_u64(function_coverage_info.function_source_hash);
208-
let bitmap_bytes = bx.const_u32(function_coverage_info.mcdc_bitmap_bytes);
209-
let max_decision_depth = function_coverage_info.mcdc_max_decision_depth;
210-
let cond_bitmap = bx.mcdc_parameters(fn_name, hash, bitmap_bytes, max_decision_depth as u32);
211-
bx.coverage_context()
212-
.expect("already checked above")
213-
.mcdc_condition_bitmap_map
214-
.borrow_mut()
215-
.insert(instance, cond_bitmap);
216-
}
217-
218228
/// Calls llvm::createPGOFuncNameVar() with the given function instance's
219229
/// mangled function name. The LLVM API returns an llvm::GlobalVariable
220230
/// containing the function name, with the specific variable name and linkage

compiler/rustc_codegen_ssa/src/mir/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,10 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
259259
// Apply debuginfo to the newly allocated locals.
260260
fx.debug_introduce_locals(&mut start_bx);
261261

262+
// If the backend supports coverage, and coverage is enabled for this function,
263+
// do any necessary start-of-function codegen (e.g. locals for MC/DC bitmaps).
264+
start_bx.init_coverage(instance);
265+
262266
// The builders will be created separately for each basic block at `codegen_block`.
263267
// So drop the builder of `start_llbb` to avoid having two at the same time.
264268
drop(start_bx);

compiler/rustc_codegen_ssa/src/traits/coverageinfo.rs

+5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ use rustc_middle::mir::coverage::CoverageKind;
33
use rustc_middle::ty::Instance;
44

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

compiler/rustc_middle/src/mir/coverage.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ pub struct FunctionCoverageInfo {
277277
pub mappings: Vec<Mapping>,
278278
/// The depth of the deepest decision is used to know how many
279279
/// temp condbitmaps should be allocated for the function.
280-
pub mcdc_max_decision_depth: u16,
280+
pub mcdc_num_condition_bitmaps: usize,
281281
}
282282

283283
/// Branch information recorded during THIR-to-MIR lowering, and stored in MIR.

compiler/rustc_mir_transform/src/coverage/mod.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -102,23 +102,23 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir:
102102

103103
inject_mcdc_statements(mir_body, &basic_coverage_blocks, &coverage_spans);
104104

105-
let mcdc_max_decision_depth = coverage_spans
105+
let mcdc_num_condition_bitmaps = coverage_spans
106106
.mappings
107107
.iter()
108108
.filter_map(|bcb_mapping| match bcb_mapping.kind {
109109
BcbMappingKind::MCDCDecision { decision_depth, .. } => Some(decision_depth),
110110
_ => None,
111111
})
112112
.max()
113-
.unwrap_or(0);
113+
.map_or(0, |max| usize::from(max) + 1);
114114

115115
mir_body.function_coverage_info = Some(Box::new(FunctionCoverageInfo {
116116
function_source_hash: hir_info.function_source_hash,
117117
num_counters: coverage_counters.num_counters(),
118118
mcdc_bitmap_bytes: coverage_spans.test_vector_bitmap_bytes(),
119119
expressions: coverage_counters.into_expressions(),
120120
mappings,
121-
mcdc_max_decision_depth,
121+
mcdc_num_condition_bitmaps,
122122
}));
123123
}
124124

0 commit comments

Comments
 (0)