Skip to content

Commit 9809114

Browse files
author
zhuyunxing
committed
coverage. Lowering MC/DC statements to llvm-ir
1 parent b48a48c commit 9809114

File tree

8 files changed

+297
-3
lines changed

8 files changed

+297
-3
lines changed

compiler/rustc_codegen_gcc/src/builder.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1737,6 +1737,38 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> {
17371737
) {
17381738
unimplemented!();
17391739
}
1740+
1741+
fn mcdc_parameters(
1742+
&mut self,
1743+
_fn_name: Self::Value,
1744+
_hash: Self::Value,
1745+
_bitmap_bytes: Self::Value,
1746+
) -> Self::Value {
1747+
unimplemented!();
1748+
}
1749+
1750+
fn mcdc_tvbitmap_update(
1751+
&mut self,
1752+
_fn_name: Self::Value,
1753+
_hash: Self::Value,
1754+
_bitmap_bytes: Self::Value,
1755+
_bitmap_index: Self::Value,
1756+
_mcdc_temp: Self::Value,
1757+
) {
1758+
unimplemented!();
1759+
}
1760+
1761+
fn mcdc_condbitmap_update(
1762+
&mut self,
1763+
_fn_name: Self::Value,
1764+
_hash: Self::Value,
1765+
_cond_loc: Self::Value,
1766+
_mcdc_temp: Self::Value,
1767+
_bool_value: Self::Value,
1768+
) {
1769+
unimplemented!();
1770+
}
1771+
17401772
}
17411773

17421774
impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {

compiler/rustc_codegen_llvm/src/builder.rs

Lines changed: 125 additions & 1 deletion
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};
@@ -1240,6 +1240,130 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> {
12401240
}
12411241
}
12421242

1243+
fn mcdc_parameters(
1244+
&mut self,
1245+
fn_name: Self::Value,
1246+
hash: Self::Value,
1247+
bitmap_bytes: Self::Value,
1248+
) -> &'ll Value {
1249+
debug!("mcdc_parameters() with args ({:?}, {:?}, {:?})", fn_name, hash, bitmap_bytes);
1250+
1251+
assert!(llvm_util::get_version() >= (18, 0, 0), "MCDC intrinsics require LLVM 18 or later");
1252+
1253+
let llfn = unsafe { llvm::LLVMRustGetInstrProfMCDCParametersIntrinsic(self.cx().llmod) };
1254+
let llty = self.cx.type_func(
1255+
&[self.cx.type_ptr(), self.cx.type_i64(), self.cx.type_i32()],
1256+
self.cx.type_void(),
1257+
);
1258+
let args = &[fn_name, hash, bitmap_bytes];
1259+
let args = self.check_call("call", llty, llfn, args);
1260+
1261+
unsafe {
1262+
let _ = llvm::LLVMRustBuildCall(
1263+
self.llbuilder,
1264+
llty,
1265+
llfn,
1266+
args.as_ptr() as *const &llvm::Value,
1267+
args.len() as c_uint,
1268+
[].as_ptr(),
1269+
0 as c_uint,
1270+
);
1271+
// Create condition bitmap named `mcdc.addr`.
1272+
let mut bx = Builder::with_cx(self.cx);
1273+
bx.position_at_start(llvm::LLVMGetFirstBasicBlock(self.llfn()));
1274+
let cond_bitmap = {
1275+
let alloca =
1276+
llvm::LLVMBuildAlloca(bx.llbuilder, bx.cx.type_i32(), c"mcdc.addr".as_ptr());
1277+
llvm::LLVMSetAlignment(alloca, 4);
1278+
alloca
1279+
};
1280+
bx.store(self.const_i32(0), cond_bitmap, self.tcx().data_layout.i32_align.abi);
1281+
cond_bitmap
1282+
}
1283+
}
1284+
1285+
fn mcdc_tvbitmap_update(
1286+
&mut self,
1287+
fn_name: Self::Value,
1288+
hash: Self::Value,
1289+
bitmap_bytes: Self::Value,
1290+
bitmap_index: Self::Value,
1291+
mcdc_temp: Self::Value,
1292+
) {
1293+
debug!(
1294+
"mcdc_tvbitmap_update() with args ({:?}, {:?}, {:?}, {:?}, {:?})",
1295+
fn_name, hash, bitmap_bytes, bitmap_index, mcdc_temp
1296+
);
1297+
assert!(llvm_util::get_version() >= (18, 0, 0), "MCDC intrinsics require LLVM 18 or later");
1298+
1299+
let llfn =
1300+
unsafe { llvm::LLVMRustGetInstrProfMCDCTVBitmapUpdateIntrinsic(self.cx().llmod) };
1301+
let llty = self.cx.type_func(
1302+
&[
1303+
self.cx.type_ptr(),
1304+
self.cx.type_i64(),
1305+
self.cx.type_i32(),
1306+
self.cx.type_i32(),
1307+
self.cx.type_ptr(),
1308+
],
1309+
self.cx.type_void(),
1310+
);
1311+
let args = &[fn_name, hash, bitmap_bytes, bitmap_index, mcdc_temp];
1312+
let args = self.check_call("call", llty, llfn, args);
1313+
unsafe {
1314+
let _ = llvm::LLVMRustBuildCall(
1315+
self.llbuilder,
1316+
llty,
1317+
llfn,
1318+
args.as_ptr() as *const &llvm::Value,
1319+
args.len() as c_uint,
1320+
[].as_ptr(),
1321+
0 as c_uint,
1322+
);
1323+
}
1324+
let i32_align = self.tcx().data_layout.i32_align.abi;
1325+
self.store(self.const_i32(0), mcdc_temp, i32_align);
1326+
}
1327+
1328+
fn mcdc_condbitmap_update(
1329+
&mut self,
1330+
fn_name: Self::Value,
1331+
hash: Self::Value,
1332+
cond_loc: Self::Value,
1333+
mcdc_temp: Self::Value,
1334+
bool_value: Self::Value,
1335+
) {
1336+
debug!(
1337+
"mcdc_condbitmap_update() with args ({:?}, {:?}, {:?}, {:?}, {:?})",
1338+
fn_name, hash, cond_loc, mcdc_temp, bool_value
1339+
);
1340+
assert!(llvm_util::get_version() >= (18, 0, 0), "MCDC intrinsics require LLVM 18 or later");
1341+
let llfn = unsafe { llvm::LLVMRustGetInstrProfMCDCCondBitmapIntrinsic(self.cx().llmod) };
1342+
let llty = self.cx.type_func(
1343+
&[
1344+
self.cx.type_ptr(),
1345+
self.cx.type_i64(),
1346+
self.cx.type_i32(),
1347+
self.cx.type_ptr(),
1348+
self.cx.type_i1(),
1349+
],
1350+
self.cx.type_void(),
1351+
);
1352+
let args = &[fn_name, hash, cond_loc, mcdc_temp, bool_value];
1353+
self.check_call("call", llty, llfn, args);
1354+
unsafe {
1355+
let _ = llvm::LLVMRustBuildCall(
1356+
self.llbuilder,
1357+
llty,
1358+
llfn,
1359+
args.as_ptr() as *const &llvm::Value,
1360+
args.len() as c_uint,
1361+
[].as_ptr(),
1362+
0 as c_uint,
1363+
);
1364+
}
1365+
}
1366+
12431367
fn call(
12441368
&mut self,
12451369
llty: &'ll Type,

compiler/rustc_codegen_llvm/src/coverageinfo/mod.rs

Lines changed: 69 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,10 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
9098
return;
9199
};
92100

101+
if function_coverage_info.mcdc_bitmap_bytes > 0 {
102+
ensure_mcdc_parameters(bx, instance, function_coverage_info);
103+
}
104+
93105
let Some(coverage_context) = bx.coverage_context() else { return };
94106
let mut coverage_map = coverage_context.function_coverage_map.borrow_mut();
95107
let func_coverage = coverage_map
@@ -131,10 +143,66 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
131143
CoverageKind::ExpressionUsed { id } => {
132144
func_coverage.mark_expression_id_seen(id);
133145
}
146+
CoverageKind::CondBitmapUpdate { id, value, .. } => {
147+
drop(coverage_map);
148+
assert_ne!(
149+
id.as_u32(),
150+
0,
151+
"ConditionId of evaluated conditions should never be zero"
152+
);
153+
let cond_bitmap = coverage_context
154+
.try_get_mcdc_condition_bitmap(&instance)
155+
.expect("mcdc cond bitmap should have been allocated for updating");
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::TestVectorBitmapUpdate { bitmap_idx } => {
163+
drop(coverage_map);
164+
let cond_bitmap = coverage_context
165+
.try_get_mcdc_condition_bitmap(&instance)
166+
.expect("mcdc cond bitmap should have been allocated for merging into the global bitmap");
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+
}
134181
}
135182
}
136183
}
137184

185+
fn ensure_mcdc_parameters<'ll, 'tcx>(
186+
bx: &mut Builder<'_, 'll, 'tcx>,
187+
instance: Instance<'tcx>,
188+
function_coverage_info: &FunctionCoverageInfo,
189+
) {
190+
let Some(cx) = bx.coverage_context() else { return };
191+
if cx.mcdc_condition_bitmap_map.borrow().contains_key(&instance) {
192+
return;
193+
}
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+
.expect("already checked above")
201+
.mcdc_condition_bitmap_map
202+
.borrow_mut()
203+
.insert(instance, cond_bitmap);
204+
}
205+
138206
/// Calls llvm::createPGOFuncNameVar() with the given function instance's
139207
/// mangled function name. The LLVM API returns an llvm::GlobalVariable
140208
/// containing the function name, with the specific variable name and linkage

compiler/rustc_codegen_llvm/src/llvm/ffi.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1631,6 +1631,10 @@ extern "C" {
16311631

16321632
// Miscellaneous instructions
16331633
pub fn LLVMRustGetInstrProfIncrementIntrinsic(M: &Module) -> &Value;
1634+
pub fn LLVMRustGetInstrProfMCDCParametersIntrinsic(M: &Module) -> &Value;
1635+
pub fn LLVMRustGetInstrProfMCDCTVBitmapUpdateIntrinsic(M: &Module) -> &Value;
1636+
pub fn LLVMRustGetInstrProfMCDCCondBitmapIntrinsic(M: &Module) -> &Value;
1637+
16341638
pub fn LLVMRustBuildCall<'a>(
16351639
B: &Builder<'a>,
16361640
Ty: &'a Type,

compiler/rustc_codegen_ssa/src/traits/builder.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,31 @@ pub trait BuilderMethods<'a, 'tcx>:
405405
index: Self::Value,
406406
);
407407

408+
fn mcdc_parameters(
409+
&mut self,
410+
fn_name: Self::Value,
411+
hash: Self::Value,
412+
bitmap_bytes: Self::Value,
413+
) -> Self::Value;
414+
415+
fn mcdc_condbitmap_update(
416+
&mut self,
417+
fn_name: Self::Value,
418+
hash: Self::Value,
419+
cond_loc: Self::Value,
420+
mcdc_temp: Self::Value,
421+
bool_value: Self::Value,
422+
);
423+
424+
fn mcdc_tvbitmap_update(
425+
&mut self,
426+
fn_name: Self::Value,
427+
hash: Self::Value,
428+
bitmap_bytes: Self::Value,
429+
bitmap_index: Self::Value,
430+
mcdc_temp: Self::Value,
431+
);
432+
408433
fn call(
409434
&mut self,
410435
llty: Self::Type,

compiler/rustc_llvm/llvm-wrapper/RustWrapper.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1546,6 +1546,33 @@ extern "C" LLVMValueRef LLVMRustGetInstrProfIncrementIntrinsic(LLVMModuleRef M)
15461546
unwrap(M), llvm::Intrinsic::instrprof_increment));
15471547
}
15481548

1549+
extern "C" LLVMValueRef LLVMRustGetInstrProfMCDCParametersIntrinsic(LLVMModuleRef M) {
1550+
#if LLVM_VERSION_GE(18, 0)
1551+
return wrap(llvm::Intrinsic::getDeclaration(
1552+
unwrap(M), llvm::Intrinsic::instrprof_mcdc_parameters));
1553+
#else
1554+
report_fatal_error("LLVM 18.0 is required for mcdc intrinsic functions");
1555+
#endif
1556+
}
1557+
1558+
extern "C" LLVMValueRef LLVMRustGetInstrProfMCDCTVBitmapUpdateIntrinsic(LLVMModuleRef M) {
1559+
#if LLVM_VERSION_GE(18, 0)
1560+
return wrap(llvm::Intrinsic::getDeclaration(
1561+
unwrap(M), llvm::Intrinsic::instrprof_mcdc_tvbitmap_update));
1562+
#else
1563+
report_fatal_error("LLVM 18.0 is required for mcdc intrinsic functions");
1564+
#endif
1565+
}
1566+
1567+
extern "C" LLVMValueRef LLVMRustGetInstrProfMCDCCondBitmapIntrinsic(LLVMModuleRef M) {
1568+
#if LLVM_VERSION_GE(18, 0)
1569+
return wrap(llvm::Intrinsic::getDeclaration(
1570+
unwrap(M), llvm::Intrinsic::instrprof_mcdc_condbitmap_update));
1571+
#else
1572+
report_fatal_error("LLVM 18.0 is required for mcdc intrinsic functions");
1573+
#endif
1574+
}
1575+
15491576
extern "C" LLVMValueRef LLVMRustBuildMemCpy(LLVMBuilderRef B,
15501577
LLVMValueRef Dst, unsigned DstAlign,
15511578
LLVMValueRef Src, unsigned SrcAlign,

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_transform/src/coverage/query.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,17 @@ fn coverage_ids_info<'tcx>(
6161
.max()
6262
.unwrap_or(CounterId::ZERO);
6363

64-
CoverageIdsInfo { max_counter_id }
64+
let mcdc_bitmap_bytes = mir_body
65+
.coverage_branch_info
66+
.as_deref()
67+
.map(|info| {
68+
info.mcdc_decision_spans
69+
.iter()
70+
.fold(0, |acc, decision| acc + (1_u32 << decision.conditions_num).div_ceil(8))
71+
})
72+
.unwrap_or_default();
73+
74+
CoverageIdsInfo { max_counter_id, mcdc_bitmap_bytes }
6575
}
6676

6777
fn all_coverage_in_mir_body<'a, 'tcx>(

0 commit comments

Comments
 (0)