Skip to content

Commit

Permalink
Rollup merge of rust-lang#132124 - Zalathar:consolidate-covstar, r=ji…
Browse files Browse the repository at this point in the history
…eyouxu

coverage: Consolidate creation of covmap/covfun records

This code for creating covmap/covfun records during codegen was split across multiple functions and files for dubious historical reasons. Having it all in one place makes it easier to follow.

This PR also includes two semi-related cleanups:
- Getting the codegen context's `coverage_cx` state is made infallible, since it should always exist when running the code paths that need it.
- The value of `covfun_section_name` is saved in the codegen context, since it never changes at runtime, and the code that needs it has access to the context anyway.

---

Background: Coverage instrumentation generates two kinds of metadata that are embedded in the final binary. There is per-CGU information that goes in the `__llvm_covmap` linker section, and per-function information that goes in the `__llvm_covfun` section (except on Windows, where slightly different section names are used).
  • Loading branch information
jieyouxu authored Oct 26, 2024
2 parents 80d0d92 + 0d653a5 commit 35d4b99
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 144 deletions.
8 changes: 4 additions & 4 deletions compiler/rustc_codegen_llvm/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ pub(crate) struct CodegenCx<'ll, 'tcx> {

pub isize_ty: &'ll Type,

/// Extra codegen state needed when coverage instrumentation is enabled.
pub coverage_cx: Option<coverageinfo::CrateCoverageContext<'ll, 'tcx>>,
pub dbg_cx: Option<debuginfo::CodegenUnitDebugContext<'ll, 'tcx>>,

Expand Down Expand Up @@ -592,11 +593,10 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
&self.statics_to_rauw
}

/// Extra state that is only available when coverage instrumentation is enabled.
#[inline]
pub(crate) fn coverage_context(
&self,
) -> Option<&coverageinfo::CrateCoverageContext<'ll, 'tcx>> {
self.coverage_cx.as_ref()
pub(crate) fn coverage_cx(&self) -> &coverageinfo::CrateCoverageContext<'ll, 'tcx> {
self.coverage_cx.as_ref().expect("only called when coverage instrumentation is enabled")
}

pub(crate) fn create_used_variable_impl(&self, name: &'static CStr, values: &[&'ll Value]) {
Expand Down
100 changes: 65 additions & 35 deletions compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
use std::ffi::CStr;
use std::ffi::CString;

use itertools::Itertools as _;
use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods, ConstCodegenMethods};
use rustc_abi::Align;
use rustc_codegen_ssa::traits::{
BaseTypeCodegenMethods, ConstCodegenMethods, StaticCodegenMethods,
};
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_index::IndexVec;
Expand All @@ -10,6 +13,7 @@ use rustc_middle::ty::{self, TyCtxt};
use rustc_middle::{bug, mir};
use rustc_span::Symbol;
use rustc_span::def_id::DefIdSet;
use rustc_target::spec::HasTargetSpec;
use tracing::debug;

use crate::common::CodegenCx;
Expand Down Expand Up @@ -50,11 +54,7 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
add_unused_functions(cx);
}

let function_coverage_map = match cx.coverage_context() {
Some(ctx) => ctx.take_function_coverage_map(),
None => return,
};

let function_coverage_map = cx.coverage_cx().take_function_coverage_map();
if function_coverage_map.is_empty() {
// This module has no functions with coverage instrumentation
return;
Expand All @@ -78,11 +78,9 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {

// Generate the coverage map header, which contains the filenames used by
// this CGU's coverage mappings, and store it in a well-known global.
let cov_data_val = generate_coverage_map(cx, covmap_version, filenames_size, filenames_val);
coverageinfo::save_cov_data_to_mod(cx, cov_data_val);
generate_covmap_record(cx, covmap_version, filenames_size, filenames_val);

let mut unused_function_names = Vec::new();
let covfun_section_name = coverageinfo::covfun_section_name(cx);

// Encode coverage mappings and generate function records
for (instance, function_coverage) in function_coverage_entries {
Expand Down Expand Up @@ -111,9 +109,8 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
unused_function_names.push(mangled_function_name);
}

save_function_record(
generate_covfun_record(
cx,
&covfun_section_name,
mangled_function_name,
source_hash,
filenames_ref,
Expand Down Expand Up @@ -308,15 +305,15 @@ fn encode_mappings_for_function(
})
}

/// Construct coverage map header and the array of function records, and combine them into the
/// coverage map. Save the coverage map data into the LLVM IR as a static global using a
/// specific, well-known section and name.
fn generate_coverage_map<'ll>(
/// Generates the contents of the covmap record for this CGU, which mostly
/// consists of a header and a list of filenames. The record is then stored
/// as a global variable in the `__llvm_covmap` section.
fn generate_covmap_record<'ll>(
cx: &CodegenCx<'ll, '_>,
version: u32,
filenames_size: usize,
filenames_val: &'ll llvm::Value,
) -> &'ll llvm::Value {
) {
debug!("cov map: filenames_size = {}, 0-based version = {}", filenames_size, version);

// Create the coverage data header (Note, fields 0 and 2 are now always zero,
Expand All @@ -331,15 +328,37 @@ fn generate_coverage_map<'ll>(
);

// Create the complete LLVM coverage data value to add to the LLVM IR
cx.const_struct(&[cov_data_header_val, filenames_val], /*packed=*/ false)
let covmap_data =
cx.const_struct(&[cov_data_header_val, filenames_val], /*packed=*/ false);

let covmap_var_name = CString::new(llvm::build_byte_buffer(|s| unsafe {
llvm::LLVMRustCoverageWriteMappingVarNameToString(s);
}))
.unwrap();
debug!("covmap var name: {:?}", covmap_var_name);

let covmap_section_name = CString::new(llvm::build_byte_buffer(|s| unsafe {
llvm::LLVMRustCoverageWriteMapSectionNameToString(cx.llmod, s);
}))
.expect("covmap section name should not contain NUL");
debug!("covmap section name: {:?}", covmap_section_name);

let llglobal = llvm::add_global(cx.llmod, cx.val_ty(covmap_data), &covmap_var_name);
llvm::set_initializer(llglobal, covmap_data);
llvm::set_global_constant(llglobal, true);
llvm::set_linkage(llglobal, llvm::Linkage::PrivateLinkage);
llvm::set_section(llglobal, &covmap_section_name);
// LLVM's coverage mapping format specifies 8-byte alignment for items in this section.
// <https://llvm.org/docs/CoverageMappingFormat.html>
llvm::set_alignment(llglobal, Align::EIGHT);
cx.add_used_global(llglobal);
}

/// Construct a function record and combine it with the function's coverage mapping data.
/// Save the function record into the LLVM IR as a static global using a
/// specific, well-known section and name.
fn save_function_record(
/// Generates the contents of the covfun record for this function, which
/// contains the function's coverage mapping data. The record is then stored
/// as a global variable in the `__llvm_covfun` section.
fn generate_covfun_record(
cx: &CodegenCx<'_, '_>,
covfun_section_name: &CStr,
mangled_function_name: &str,
source_hash: u64,
filenames_ref: u64,
Expand All @@ -366,13 +385,28 @@ fn save_function_record(
/*packed=*/ true,
);

coverageinfo::save_func_record_to_mod(
cx,
covfun_section_name,
func_name_hash,
func_record_val,
is_used,
);
// Choose a variable name to hold this function's covfun data.
// 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
// linker sees both it won't discard the used copy's data.
let func_record_var_name =
CString::new(format!("__covrec_{:X}{}", func_name_hash, if is_used { "u" } else { "" }))
.unwrap();
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);
llvm::set_initializer(llglobal, func_record_val);
llvm::set_global_constant(llglobal, true);
llvm::set_linkage(llglobal, llvm::Linkage::LinkOnceODRLinkage);
llvm::set_visibility(llglobal, llvm::Visibility::Hidden);
llvm::set_section(llglobal, cx.covfun_section_name());
// LLVM's coverage mapping format specifies 8-byte alignment for items in this section.
// <https://llvm.org/docs/CoverageMappingFormat.html>
llvm::set_alignment(llglobal, Align::EIGHT);
if cx.target_spec().supports_comdat() {
llvm::set_comdat(cx.llmod, llglobal, &func_record_var_name);
}
cx.add_used_global(llglobal);
}

/// Each CGU will normally only emit coverage metadata for the functions that it actually generates.
Expand Down Expand Up @@ -504,9 +538,5 @@ fn add_unused_function_coverage<'tcx>(
// zero, because none of its counters/expressions are marked as seen.
let function_coverage = FunctionCoverageCollector::unused(instance, function_coverage_info);

if let Some(coverage_context) = cx.coverage_context() {
coverage_context.function_coverage_map.borrow_mut().insert(instance, function_coverage);
} else {
bug!("Could not get the `coverage_context`");
}
cx.coverage_cx().function_coverage_map.borrow_mut().insert(instance, function_coverage);
}
Loading

0 comments on commit 35d4b99

Please sign in to comment.