Skip to content

Commit eb2f8af

Browse files
author
zhuyunxing
committed
coverage. Generate mcdc instrument when lowering to ir and add warning for decisions with too many conditions
1 parent 3609cec commit eb2f8af

File tree

13 files changed

+214
-57
lines changed

13 files changed

+214
-57
lines changed

compiler/rustc_codegen_llvm/src/builder.rs

Lines changed: 15 additions & 4 deletions
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, LayoutError, LayoutOfHelpers, TyAndLayout,
20+
FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasTyCtxt, LayoutError, LayoutOfHelpers, TyAndLayout,
2121
};
2222
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
2323
use rustc_sanitizers::{cfi, kcfi};
@@ -1245,7 +1245,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
12451245
fn_name: Self::Value,
12461246
hash: Self::Value,
12471247
bitmap_bytes: Self::Value,
1248-
) {
1248+
) -> &'ll Value {
12491249
debug!("mcdc_parameters() with args ({:?}, {:?}, {:?})", fn_name, hash, bitmap_bytes);
12501250

12511251
let llfn = unsafe { llvm::LLVMRustGetInstrProfMCDCParametersIntrinsic(self.cx().llmod) };
@@ -1266,6 +1266,17 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
12661266
[].as_ptr(),
12671267
0 as c_uint,
12681268
);
1269+
// Create condition bitmap named `mcdc.addr`.
1270+
let mut bx = Builder::with_cx(self.cx);
1271+
bx.position_at_start(llvm::LLVMGetFirstBasicBlock(self.llfn()));
1272+
let cond_bitmap = {
1273+
let alloca =
1274+
llvm::LLVMBuildAlloca(bx.llbuilder, bx.cx.type_i32(), c"mcdc.addr".as_ptr());
1275+
llvm::LLVMSetAlignment(alloca, 4);
1276+
alloca
1277+
};
1278+
bx.store(self.const_i32(0), cond_bitmap, self.tcx().data_layout.i32_align.abi);
1279+
cond_bitmap
12691280
}
12701281
}
12711282

@@ -1324,12 +1335,12 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
13241335
self.cx.type_i64(),
13251336
self.cx.type_i32(),
13261337
self.cx.type_ptr(),
1327-
self.cx.type_bool(),
1338+
self.cx.type_i1(),
13281339
],
13291340
self.cx.type_void(),
13301341
);
13311342
let args = &[fn_name, hash, cond_loc, mcdc_temp, bool_value];
1332-
let args = self.check_call("call", llty, llfn, args);
1343+
self.check_call("call", llty, llfn, args);
13331344
unsafe {
13341345
let _ = llvm::LLVMRustBuildCall(
13351346
self.llbuilder,

compiler/rustc_codegen_llvm/src/coverageinfo/map_data.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use crate::coverageinfo::ffi::{Counter, CounterExpression, ExprKind};
2-
32
use rustc_data_structures::captures::Captures;
43
use rustc_data_structures::fx::FxIndexSet;
54
use rustc_index::bit_set::BitSet;
@@ -74,6 +73,7 @@ impl<'tcx> FunctionCoverageCollector<'tcx> {
7473
Self {
7574
function_coverage_info,
7675
is_used,
76+
7777
counters_seen: BitSet::new_empty(num_counters),
7878
expressions_seen,
7979
}

compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ 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;
16+
use rustc_middle::mir::coverage::{CoverageKind, FunctionCoverageInfo};
1717
use rustc_middle::ty::layout::HasTyCtxt;
1818
use rustc_middle::ty::Instance;
1919
use rustc_target::abi::Align;
@@ -30,13 +30,15 @@ pub struct CrateCoverageContext<'ll, 'tcx> {
3030
pub(crate) function_coverage_map:
3131
RefCell<FxIndexMap<Instance<'tcx>, FunctionCoverageCollector<'tcx>>>,
3232
pub(crate) pgo_func_name_var_map: RefCell<FxHashMap<Instance<'tcx>, &'ll llvm::Value>>,
33+
pub(crate) mcdc_condition_bitmap_map: RefCell<FxHashMap<Instance<'tcx>, &'ll llvm::Value>>,
3334
}
3435

3536
impl<'ll, 'tcx> CrateCoverageContext<'ll, 'tcx> {
3637
pub fn new() -> Self {
3738
Self {
3839
function_coverage_map: Default::default(),
3940
pgo_func_name_var_map: Default::default(),
41+
mcdc_condition_bitmap_map: Default::default(),
4042
}
4143
}
4244

@@ -45,6 +47,12 @@ impl<'ll, 'tcx> CrateCoverageContext<'ll, 'tcx> {
4547
) -> FxIndexMap<Instance<'tcx>, FunctionCoverageCollector<'tcx>> {
4648
self.function_coverage_map.replace(FxIndexMap::default())
4749
}
50+
51+
/// LLVM use a temp value to record evaluated mcdc test vector of each decision, which is called condition bitmap.
52+
/// This value is named `mcdc.addr` (same as clang) and is a 32-bit integer.
53+
fn try_get_mcdc_condition_bitmap(&self, instance: &Instance<'tcx>) -> Option<&'ll llvm::Value> {
54+
self.mcdc_condition_bitmap_map.borrow().get(instance).copied()
55+
}
4856
}
4957

5058
// These methods used to be part of trait `CoverageInfoMethods`, which no longer
@@ -90,6 +98,14 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
9098
return;
9199
};
92100

101+
match *kind {
102+
// Ensure mcdc parameters are properly initialized for these operations.
103+
CoverageKind::UpdateCondBitmap { .. } | CoverageKind::UpdateTestVector { .. } => {
104+
ensure_mcdc_parameters(bx, instance, function_coverage_info)
105+
}
106+
_ => {}
107+
}
108+
93109
let Some(coverage_context) = bx.coverage_context() else { return };
94110
let mut coverage_map = coverage_context.function_coverage_map.borrow_mut();
95111
let func_coverage = coverage_map
@@ -131,10 +147,63 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
131147
CoverageKind::ExpressionUsed { id } => {
132148
func_coverage.mark_expression_id_seen(id);
133149
}
150+
151+
CoverageKind::UpdateCondBitmap { id, value } => {
152+
drop(coverage_map);
153+
let cond_bitmap = coverage_context
154+
.try_get_mcdc_condition_bitmap(&instance)
155+
.expect("mcdc cond bitmap must be set before being updated");
156+
let cond_loc = bx.const_i32(id.as_u32() as i32 - 1);
157+
let bool_value = bx.const_bool(value);
158+
let fn_name = bx.get_pgo_func_name_var(instance);
159+
let hash = bx.const_u64(function_coverage_info.function_source_hash);
160+
bx.mcdc_condbitmap_update(fn_name, hash, cond_loc, cond_bitmap, bool_value);
161+
}
162+
CoverageKind::UpdateTestVector { bitmap_idx } => {
163+
drop(coverage_map);
164+
let cond_bitmap = coverage_context
165+
.try_get_mcdc_condition_bitmap(&instance)
166+
.expect("mcdc cond bitmap must be set before being updated");
167+
let bitmap_bytes = bx.tcx().coverage_ids_info(instance.def).mcdc_bitmap_bytes;
168+
assert!(bitmap_idx < bitmap_bytes, "bitmap index of the decision out of range");
169+
assert!(
170+
bitmap_bytes <= function_coverage_info.mcdc_bitmap_bytes,
171+
"bitmap length disagreement: query says {bitmap_bytes} but function info only has {}",
172+
function_coverage_info.mcdc_bitmap_bytes
173+
);
174+
175+
let fn_name = bx.get_pgo_func_name_var(instance);
176+
let hash = bx.const_u64(function_coverage_info.function_source_hash);
177+
let bitmap_bytes = bx.const_u32(bitmap_bytes);
178+
let bitmap_index = bx.const_u32(bitmap_idx);
179+
bx.mcdc_tvbitmap_update(fn_name, hash, bitmap_bytes, bitmap_index, cond_bitmap);
180+
bx.mcdc_condbitmap_reset(cond_bitmap);
181+
}
134182
}
135183
}
136184
}
137185

186+
fn ensure_mcdc_parameters<'ll, 'tcx>(
187+
bx: &mut Builder<'_, 'll, 'tcx>,
188+
instance: Instance<'tcx>,
189+
function_coverage_info: &FunctionCoverageInfo,
190+
) {
191+
if bx
192+
.coverage_context()
193+
.is_some_and(|cx| !cx.mcdc_condition_bitmap_map.borrow().contains_key(&instance))
194+
{
195+
let fn_name = bx.get_pgo_func_name_var(instance);
196+
let hash = bx.const_u64(function_coverage_info.function_source_hash);
197+
let bitmap_bytes = bx.const_u32(function_coverage_info.mcdc_bitmap_bytes);
198+
let cond_bitmap = bx.mcdc_parameters(fn_name, hash, bitmap_bytes);
199+
bx.coverage_context()
200+
.unwrap()
201+
.mcdc_condition_bitmap_map
202+
.borrow_mut()
203+
.insert(instance, cond_bitmap);
204+
}
205+
}
206+
138207
/// Calls llvm::createPGOFuncNameVar() with the given function instance's
139208
/// mangled function name. The LLVM API returns an llvm::GlobalVariable
140209
/// containing the function name, with the specific variable name and linkage

compiler/rustc_codegen_ssa/src/traits/builder.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,7 @@ pub trait BuilderMethods<'a, 'tcx>:
410410
fn_name: Self::Value,
411411
hash: Self::Value,
412412
bitmap_bytes: Self::Value,
413-
);
413+
) -> Self::Value;
414414

415415
fn mcdc_condbitmap_update(
416416
&mut self,

compiler/rustc_middle/src/mir/coverage.rs

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,9 @@ pub enum CoverageKind {
112112
///
113113
/// If this statement does not survive MIR optimizations, any mappings that
114114
/// refer to this counter can have those references simplified to zero.
115-
CounterIncrement { id: CounterId },
115+
CounterIncrement {
116+
id: CounterId,
117+
},
116118

117119
/// Marks the point in MIR control-flow represented by a coverage expression.
118120
///
@@ -122,7 +124,18 @@ pub enum CoverageKind {
122124
/// (This is only inserted for expression IDs that are directly used by
123125
/// mappings. Intermediate expressions with no direct mappings are
124126
/// retained/zeroed based on whether they are transitively used.)
125-
ExpressionUsed { id: ExpressionId },
127+
ExpressionUsed {
128+
id: ExpressionId,
129+
},
130+
131+
UpdateCondBitmap {
132+
id: ConditionId,
133+
value: bool,
134+
},
135+
136+
UpdateTestVector {
137+
bitmap_idx: u32,
138+
},
126139
}
127140

128141
impl Debug for CoverageKind {
@@ -133,6 +146,10 @@ impl Debug for CoverageKind {
133146
BlockMarker { id } => write!(fmt, "BlockMarker({:?})", id.index()),
134147
CounterIncrement { id } => write!(fmt, "CounterIncrement({:?})", id.index()),
135148
ExpressionUsed { id } => write!(fmt, "ExpressionUsed({:?})", id.index()),
149+
UpdateCondBitmap { id, value } => {
150+
write!(fmt, "UpdateCondBitmap({:?}, {value})", id.index())
151+
}
152+
UpdateTestVector { bitmap_idx } => write!(fmt, "UpdateTestVector({:?})", bitmap_idx),
136153
}
137154
}
138155
}
@@ -236,7 +253,7 @@ pub struct Mapping {
236253
pub struct FunctionCoverageInfo {
237254
pub function_source_hash: u64,
238255
pub num_counters: usize,
239-
256+
pub mcdc_bitmap_bytes: u32,
240257
pub expressions: IndexVec<ExpressionId, Expression>,
241258
pub mappings: Vec<Mapping>,
242259
}
@@ -250,6 +267,7 @@ pub struct BranchInfo {
250267
/// data structures without having to scan the entire body first.
251268
pub num_block_markers: usize,
252269
pub branch_spans: Vec<BranchSpan>,
270+
pub mcdc_bitmap_bytes_num: u32,
253271
pub decision_spans: Vec<DecisionSpan>,
254272
}
255273

@@ -291,5 +309,5 @@ pub struct DecisionInfo {
291309
#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
292310
pub struct DecisionSpan {
293311
pub span: Span,
294-
pub conditions_num: u16,
312+
pub mcdc_params: DecisionInfo,
295313
}

compiler/rustc_middle/src/mir/query.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,4 +361,8 @@ pub struct CoverageIdsInfo {
361361
/// InstrumentCoverage MIR pass, if the highest-numbered counter increments
362362
/// were removed by MIR optimizations.
363363
pub max_counter_id: mir::coverage::CounterId,
364+
365+
/// Coverage codegen for mcdc needs to know the size of the global bitmap so that it can
366+
/// set the `bytemap-bytes` argument of the `llvm.instrprof.mcdc.tvbitmap.update` intrinsic.
367+
pub mcdc_bitmap_bytes: u32,
364368
}

compiler/rustc_mir_build/messages.ftl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,3 +406,5 @@ mir_build_unused_unsafe_enclosing_block_label = because it's nested under this `
406406
mir_build_variant_defined_here = not covered
407407
408408
mir_build_wrap_suggestion = consider wrapping the function body in an unsafe block
409+
410+
mir_build_exceeds_mcdc_condition_num_limit = Conditions number of the decision ({$conditions_num}) exceeds limit ({$max_conditions_num}). MCDC analysis will not count this expression.

0 commit comments

Comments
 (0)