coverage. Lowering MC/DC statements to llvm-ir

This commit is contained in:
zhuyunxing 2024-04-19 10:58:16 +08:00 committed by Deltalice
parent cf6b6cb2b4
commit 439dbfa1ec
6 changed files with 240 additions and 3 deletions

View file

@ -13,7 +13,7 @@ use rustc_codegen_ssa::traits::{
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
use rustc_llvm::RustString;
use rustc_middle::bug;
use rustc_middle::mir::coverage::CoverageKind;
use rustc_middle::mir::coverage::{CoverageKind, FunctionCoverageInfo};
use rustc_middle::ty::layout::HasTyCtxt;
use rustc_middle::ty::Instance;
use rustc_target::abi::Align;
@ -30,6 +30,7 @@ pub struct CrateCoverageContext<'ll, 'tcx> {
pub(crate) function_coverage_map:
RefCell<FxIndexMap<Instance<'tcx>, FunctionCoverageCollector<'tcx>>>,
pub(crate) pgo_func_name_var_map: RefCell<FxHashMap<Instance<'tcx>, &'ll llvm::Value>>,
pub(crate) mcdc_condition_bitmap_map: RefCell<FxHashMap<Instance<'tcx>, &'ll llvm::Value>>,
}
impl<'ll, 'tcx> CrateCoverageContext<'ll, 'tcx> {
@ -37,6 +38,7 @@ impl<'ll, 'tcx> CrateCoverageContext<'ll, 'tcx> {
Self {
function_coverage_map: Default::default(),
pgo_func_name_var_map: Default::default(),
mcdc_condition_bitmap_map: Default::default(),
}
}
@ -45,6 +47,12 @@ impl<'ll, 'tcx> CrateCoverageContext<'ll, 'tcx> {
) -> FxIndexMap<Instance<'tcx>, FunctionCoverageCollector<'tcx>> {
self.function_coverage_map.replace(FxIndexMap::default())
}
/// LLVM use a temp value to record evaluated mcdc test vector of each decision, which is called condition bitmap.
/// This value is named `mcdc.addr` (same as clang) and is a 32-bit integer.
fn try_get_mcdc_condition_bitmap(&self, instance: &Instance<'tcx>) -> Option<&'ll llvm::Value> {
self.mcdc_condition_bitmap_map.borrow().get(instance).copied()
}
}
// These methods used to be part of trait `CoverageInfoMethods`, which no longer
@ -90,6 +98,10 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
return;
};
if function_coverage_info.mcdc_bitmap_bytes > 0 {
ensure_mcdc_parameters(bx, instance, function_coverage_info);
}
let Some(coverage_context) = bx.coverage_context() else { return };
let mut coverage_map = coverage_context.function_coverage_map.borrow_mut();
let func_coverage = coverage_map
@ -131,10 +143,66 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
CoverageKind::ExpressionUsed { id } => {
func_coverage.mark_expression_id_seen(id);
}
CoverageKind::CondBitmapUpdate { id, value, .. } => {
drop(coverage_map);
assert_ne!(
id.as_u32(),
0,
"ConditionId of evaluated conditions should never be zero"
);
let cond_bitmap = coverage_context
.try_get_mcdc_condition_bitmap(&instance)
.expect("mcdc cond bitmap should have been allocated for updating");
let cond_loc = bx.const_i32(id.as_u32() as i32 - 1);
let bool_value = bx.const_bool(value);
let fn_name = bx.get_pgo_func_name_var(instance);
let hash = bx.const_u64(function_coverage_info.function_source_hash);
bx.mcdc_condbitmap_update(fn_name, hash, cond_loc, cond_bitmap, bool_value);
}
CoverageKind::TestVectorBitmapUpdate { bitmap_idx } => {
drop(coverage_map);
let cond_bitmap = coverage_context
.try_get_mcdc_condition_bitmap(&instance)
.expect("mcdc cond bitmap should have been allocated for merging into the global bitmap");
let bitmap_bytes = bx.tcx().coverage_ids_info(instance.def).mcdc_bitmap_bytes;
assert!(bitmap_idx < bitmap_bytes, "bitmap index of the decision out of range");
assert!(
bitmap_bytes <= function_coverage_info.mcdc_bitmap_bytes,
"bitmap length disagreement: query says {bitmap_bytes} but function info only has {}",
function_coverage_info.mcdc_bitmap_bytes
);
let fn_name = bx.get_pgo_func_name_var(instance);
let hash = bx.const_u64(function_coverage_info.function_source_hash);
let bitmap_bytes = bx.const_u32(bitmap_bytes);
let bitmap_index = bx.const_u32(bitmap_idx);
bx.mcdc_tvbitmap_update(fn_name, hash, bitmap_bytes, bitmap_index, cond_bitmap);
}
}
}
}
fn ensure_mcdc_parameters<'ll, 'tcx>(
bx: &mut Builder<'_, 'll, 'tcx>,
instance: Instance<'tcx>,
function_coverage_info: &FunctionCoverageInfo,
) {
let Some(cx) = bx.coverage_context() else { return };
if cx.mcdc_condition_bitmap_map.borrow().contains_key(&instance) {
return;
}
let fn_name = bx.get_pgo_func_name_var(instance);
let hash = bx.const_u64(function_coverage_info.function_source_hash);
let bitmap_bytes = bx.const_u32(function_coverage_info.mcdc_bitmap_bytes);
let cond_bitmap = bx.mcdc_parameters(fn_name, hash, bitmap_bytes);
bx.coverage_context()
.expect("already checked above")
.mcdc_condition_bitmap_map
.borrow_mut()
.insert(instance, cond_bitmap);
}
/// Calls llvm::createPGOFuncNameVar() with the given function instance's
/// mangled function name. The LLVM API returns an llvm::GlobalVariable
/// containing the function name, with the specific variable name and linkage