1
Fork 0

coverage: Tidy up creation of covfun records

This commit is contained in:
Zalathar 2024-12-12 20:28:10 +11:00
parent de53fe245d
commit 5f5745beb0
2 changed files with 33 additions and 31 deletions

View file

@ -75,7 +75,10 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
// Encode all filenames referenced by coverage mappings in this CGU. // Encode all filenames referenced by coverage mappings in this CGU.
let filenames_buffer = global_file_table.make_filenames_buffer(tcx); let filenames_buffer = global_file_table.make_filenames_buffer(tcx);
let filenames_ref = llvm_cov::hash_bytes(&filenames_buffer); // The `llvm-cov` tool uses this hash to associate each covfun record with
// its corresponding filenames table, since the final binary will typically
// contain multiple covmap records from different compilation units.
let filenames_hash = llvm_cov::hash_bytes(&filenames_buffer);
let mut unused_function_names = Vec::new(); let mut unused_function_names = Vec::new();
@ -98,7 +101,7 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
for covfun in &covfun_records { for covfun in &covfun_records {
unused_function_names.extend(covfun.mangled_function_name_if_unused()); unused_function_names.extend(covfun.mangled_function_name_if_unused());
covfun::generate_covfun_record(cx, filenames_ref, covfun) covfun::generate_covfun_record(cx, filenames_hash, covfun)
} }
// For unused functions, we need to take their mangled names and store them // For unused functions, we need to take their mangled names and store them

View file

@ -136,7 +136,7 @@ fn fill_region_tables<'tcx>(
/// as a global variable in the `__llvm_covfun` section. /// as a global variable in the `__llvm_covfun` section.
pub(crate) fn generate_covfun_record<'tcx>( pub(crate) fn generate_covfun_record<'tcx>(
cx: &CodegenCx<'_, 'tcx>, cx: &CodegenCx<'_, 'tcx>,
filenames_ref: u64, filenames_hash: u64,
covfun: &CovfunRecord<'tcx>, covfun: &CovfunRecord<'tcx>,
) { ) {
let &CovfunRecord { let &CovfunRecord {
@ -155,46 +155,45 @@ pub(crate) fn generate_covfun_record<'tcx>(
regions, regions,
); );
// Concatenate the encoded coverage mappings // A covfun record consists of four target-endian integers, followed by the
let coverage_mapping_size = coverage_mapping_buffer.len(); // encoded mapping data in bytes. Note that the length field is 32 bits.
let coverage_mapping_val = cx.const_bytes(&coverage_mapping_buffer); // <https://llvm.org/docs/CoverageMappingFormat.html#llvm-ir-representation>
// See also `src/llvm-project/clang/lib/CodeGen/CoverageMappingGen.cpp` and
// `COVMAP_V3` in `src/llvm-project/llvm/include/llvm/ProfileData/InstrProfData.inc`.
let func_name_hash = llvm_cov::hash_bytes(mangled_function_name.as_bytes()); let func_name_hash = llvm_cov::hash_bytes(mangled_function_name.as_bytes());
let func_name_hash_val = cx.const_u64(func_name_hash); let covfun_record = cx.const_struct(
let coverage_mapping_size_val = cx.const_u32(coverage_mapping_size as u32);
let source_hash_val = cx.const_u64(source_hash);
let filenames_ref_val = cx.const_u64(filenames_ref);
let func_record_val = cx.const_struct(
&[ &[
func_name_hash_val, cx.const_u64(func_name_hash),
coverage_mapping_size_val, cx.const_u32(coverage_mapping_buffer.len() as u32),
source_hash_val, cx.const_u64(source_hash),
filenames_ref_val, cx.const_u64(filenames_hash),
coverage_mapping_val, cx.const_bytes(&coverage_mapping_buffer),
], ],
/*packed=*/ true, // This struct needs to be packed, so that the 32-bit length field
// doesn't have unexpected padding.
true,
); );
// Choose a variable name to hold this function's covfun data. // Choose a variable name to hold this function's covfun data.
// Functions that are used have a suffix ("u") to distinguish them from // Functions that are used have a suffix ("u") to distinguish them from
// unused copies of the same function (from different CGUs), so that if a // unused copies of the same function (from different CGUs), so that if a
// linker sees both it won't discard the used copy's data. // linker sees both it won't discard the used copy's data.
let func_record_var_name = let u = if is_used { "u" } else { "" };
CString::new(format!("__covrec_{:X}{}", func_name_hash, if is_used { "u" } else { "" })) let covfun_var_name = CString::new(format!("__covrec_{func_name_hash:X}{u}")).unwrap();
.unwrap(); debug!("function record var name: {covfun_var_name:?}");
debug!("function record var name: {:?}", func_record_var_name);
let llglobal = llvm::add_global(cx.llmod, cx.val_ty(func_record_val), &func_record_var_name); let covfun_global = llvm::add_global(cx.llmod, cx.val_ty(covfun_record), &covfun_var_name);
llvm::set_initializer(llglobal, func_record_val); llvm::set_initializer(covfun_global, covfun_record);
llvm::set_global_constant(llglobal, true); llvm::set_global_constant(covfun_global, true);
llvm::set_linkage(llglobal, llvm::Linkage::LinkOnceODRLinkage); llvm::set_linkage(covfun_global, llvm::Linkage::LinkOnceODRLinkage);
llvm::set_visibility(llglobal, llvm::Visibility::Hidden); llvm::set_visibility(covfun_global, llvm::Visibility::Hidden);
llvm::set_section(llglobal, cx.covfun_section_name()); llvm::set_section(covfun_global, cx.covfun_section_name());
// LLVM's coverage mapping format specifies 8-byte alignment for items in this section. // LLVM's coverage mapping format specifies 8-byte alignment for items in this section.
// <https://llvm.org/docs/CoverageMappingFormat.html> // <https://llvm.org/docs/CoverageMappingFormat.html>
llvm::set_alignment(llglobal, Align::EIGHT); llvm::set_alignment(covfun_global, Align::EIGHT);
if cx.target_spec().supports_comdat() { if cx.target_spec().supports_comdat() {
llvm::set_comdat(cx.llmod, llglobal, &func_record_var_name); llvm::set_comdat(cx.llmod, covfun_global, &covfun_var_name);
} }
cx.add_used_global(llglobal);
cx.add_used_global(covfun_global);
} }