1//! For each function that was instrumented for coverage, we need to embed its
2//! corresponding coverage mapping metadata inside the `__llvm_covfun`[^win]
3//! linker section of the final binary.
4//!
5//! [^win]: On Windows the section name is `.lcovfun`.
67use std::ffi::CString;
8use std::iter;
9use std::sync::Arc;
1011use rustc_abi::Align;
12use rustc_codegen_ssa::traits::{BaseTypeCodegenMethods as _, ConstCodegenMethods};
13use rustc_index::IndexVec;
14use rustc_middle::mir::coverage::{
15BasicCoverageBlock, CounterId, CovTerm, CoverageCodegenInfo, Expression, ExpressionId, Mapping,
16MappingKind, Op,
17};
18use rustc_middle::ty::{Instance, TyCtxt};
19use rustc_span::{SourceFile, Span};
20use rustc_target::spec::HasTargetSpec;
21use tracing::debug;
2223use crate::common::CodegenCx;
24use crate::coverageinfo::mapgen::{GlobalFileTable, LocalFileId, spans};
25use crate::coverageinfo::{ffi, llvm_cov};
26use crate::llvm;
2728/// Intermediate coverage metadata for a single function, used to help build
29/// the final record that will be embedded in the `__llvm_covfun` section.
30#[derive(#[automatically_derived]
impl<'tcx> ::core::fmt::Debug for CovfunRecord<'tcx> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
let names: &'static _ =
&["_instance", "mangled_function_name", "source_hash", "is_used",
"expressions", "mappings"];
let values: &[&dyn ::core::fmt::Debug] =
&[&self._instance, &self.mangled_function_name, &self.source_hash,
&self.is_used, &self.expressions, &&self.mappings];
::core::fmt::Formatter::debug_struct_fields_finish(f, "CovfunRecord",
names, values)
}
}Debug)]
31pub(crate) struct CovfunRecord<'tcx> {
32/// Not used directly, but helpful in debug messages.
33_instance: Instance<'tcx>,
3435 mangled_function_name: &'tcx str,
36 source_hash: u64,
37 is_used: bool,
3839 expressions: Vec<ffi::CounterExpression>,
40 mappings: ResolvedMappings,
41}
4243impl<'tcx> CovfunRecord<'tcx> {
44/// Iterator that yields all source files referred to by this function's
45 /// coverage mappings. Used to build the global file table for the CGU.
46pub(crate) fn all_source_files(&self) -> impl Iterator<Item = &SourceFile> {
47self.mappings.all_source_files()
48 }
49}
5051pub(crate) fn prepare_covfun_record<'tcx>(
52 tcx: TyCtxt<'tcx>,
53 instance: Instance<'tcx>,
54 is_used: bool,
55) -> Option<CovfunRecord<'tcx>> {
56let mir_info = tcx.instance_mir(instance.def).coverage_mir_info.as_deref()?;
57let cg_info = tcx.coverage_codegen_info(instance.def)?;
5859let expressions = prepare_expressions(cg_info);
60let mappings = prepare_resolved_mappings(tcx, cg_info, is_used, &mir_info.mappings)?;
6162let covfun = CovfunRecord {
63 _instance: instance,
64 mangled_function_name: tcx.symbol_name(instance).name,
65 source_hash: if is_used { mir_info.function_source_hash } else { 0 },
66is_used,
67expressions,
68mappings,
69 };
7071Some(covfun)
72}
7374fn counter_for_term(term: CovTerm) -> ffi::Counter {
75match term {
76 CovTerm::Zero => ffi::Counter::ZERO,
77 CovTerm::Counter(id) => ffi::Counter {
78 kind: ffi::CounterKind::CounterValueReference,
79 id: CounterId::as_u32(id),
80 },
81 CovTerm::Expression(id) => {
82 ffi::Counter { kind: ffi::CounterKind::Expression, id: ExpressionId::as_u32(id) }
83 }
84 }
85}
8687/// Convert the function's coverage-counter expressions into a form suitable for FFI.
88fn prepare_expressions(cg_info: &CoverageCodegenInfo) -> Vec<ffi::CounterExpression> {
89// We know that LLVM will optimize out any unused expressions before
90 // producing the final coverage map, so there's no need to do the same
91 // thing on the Rust side unless we're confident we can do much better.
92 // (See `CounterExpressionsMinimizer` in `CoverageMappingWriter.cpp`.)
93cg_info94 .expressions
95 .iter()
96 .map(move |&Expression { lhs, op, rhs }| ffi::CounterExpression {
97 lhs: counter_for_term(lhs),
98 kind: match op {
99 Op::Add => ffi::ExprKind::Add,
100 Op::Subtract => ffi::ExprKind::Subtract,
101 },
102 rhs: counter_for_term(rhs),
103 })
104 .collect::<Vec<_>>()
105}
106107/// Intermediate representation of coverage mappings, after all mapping spans
108/// have been resolved to file coordinates (or discarded), but before producing
109/// a final [`llvm_cov::Regions`].
110///
111/// Having a separate resolution step makes it easier to handle edge cases
112/// where a function (or someday an expansion) manages to lose all of its spans,
113/// without accidentally emitting invalid covfun records containing empty files.
114#[derive(#[automatically_derived]
impl ::core::fmt::Debug for ResolvedMappings {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field3_finish(f,
"ResolvedMappings", "source_file", &self.source_file,
"code_mappings", &self.code_mappings, "branch_mappings",
&&self.branch_mappings)
}
}Debug)]
115struct ResolvedMappings {
116/// Source file for all of the [`spans::Coords`] in these mappings.
117source_file: Arc<SourceFile>,
118119 code_mappings: Vec<CodeMapping>,
120 branch_mappings: Vec<BranchMapping>,
121}
122123impl ResolvedMappings {
124fn ensure_nonempty(self) -> Option<Self> {
125let ResolvedMappings { source_file: _, code_mappings, branch_mappings } = &self;
126if code_mappings.is_empty() && branch_mappings.is_empty() { None } else { Some(self) }
127 }
128129fn all_source_files(&self) -> impl Iterator<Item = &SourceFile> {
130// FIXME(Zalathar): When expansion regions are supported, this also needs to yield
131 // any source files used by descendant expansions.
132let ResolvedMappings { source_file, code_mappings: _, branch_mappings: _ } = self;
133 iter::once(source_file.as_ref())
134 }
135}
136137/// Resolved from [`MappingKind::Code`], and the precursor to [`ffi::CodeRegion`].
138#[derive(#[automatically_derived]
impl ::core::fmt::Debug for CodeMapping {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field2_finish(f, "CodeMapping",
"coords", &self.coords, "counter", &&self.counter)
}
}Debug)]
139struct CodeMapping {
140 coords: spans::Coords,
141 counter: ffi::Counter,
142}
143144/// Resolved from [`MappingKind::Branch`], and the precursor to [`ffi::BranchRegion`].
145#[derive(#[automatically_derived]
impl ::core::fmt::Debug for BranchMapping {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field3_finish(f, "BranchMapping",
"coords", &self.coords, "true_counter", &self.true_counter,
"false_counter", &&self.false_counter)
}
}Debug)]
146struct BranchMapping {
147 coords: spans::Coords,
148 true_counter: ffi::Counter,
149 false_counter: ffi::Counter,
150}
151152fn prepare_resolved_mappings<'tcx>(
153 tcx: TyCtxt<'tcx>,
154 cg_info: &'tcx CoverageCodegenInfo,
155 is_used: bool,
156 mappings: &[Mapping],
157) -> Option<ResolvedMappings> {
158// If this function is unused, replace all counters with zero.
159let counter_for_bcb = |bcb: BasicCoverageBlock| -> ffi::Counter {
160let term = if is_used {
161cg_info.term_for_bcb[bcb].expect("every BCB in a mapping was given a term")
162 } else {
163 CovTerm::Zero164 };
165counter_for_term(term)
166 };
167168// Currently a function's mappings must all be in the same file, so use the
169 // first mapping's span to determine the file.
170let source_map = tcx.sess.source_map();
171let first_span = mappings.first()?.span;
172let source_file = source_map.lookup_source_file(first_span.lo());
173174// In rare cases, _all_ of a function's spans are discarded, and coverage
175 // codegen needs to handle that gracefully to avoid #133606.
176 // It's hard for tests to trigger this organically, so instead we set
177 // `-Zcoverage-options=discard-all-spans-in-codegen` to force it to occur.
178let discard_all = tcx.sess.coverage_options().discard_all_spans_in_codegen;
179let make_coords = |span: Span| {
180if discard_all { None } else { spans::make_coords(source_map, &source_file, span) }
181 };
182183let mut code_mappings = ::alloc::vec::Vec::new()vec![];
184let mut branch_mappings = ::alloc::vec::Vec::new()vec![];
185186for &Mapping { ref kind, span } in mappings {
187let Some(coords) = make_coords(span) else { continue };
188match *kind {
189 MappingKind::Code { bcb } => {
190 code_mappings.push(CodeMapping { coords, counter: counter_for_bcb(bcb) })
191 }
192 MappingKind::Branch { true_bcb, false_bcb } => branch_mappings.push(BranchMapping {
193 coords,
194 true_counter: counter_for_bcb(true_bcb),
195 false_counter: counter_for_bcb(false_bcb),
196 }),
197 }
198 }
199200ResolvedMappings { source_file, code_mappings, branch_mappings }.ensure_nonempty()
201}
202203/// Populates the mapping region tables for the current function's covfun record.
204fn fill_region_tables(
205 global_file_table: &GlobalFileTable,
206 mappings: &ResolvedMappings,
207 virtual_file_mapping: &mut IndexVec<LocalFileId, u32>,
208 regions: &mut llvm_cov::Regions,
209) {
210let ResolvedMappings { source_file, code_mappings, branch_mappings } = mappings;
211let Some(global_file_id) = global_file_table.get_existing_id(source_file) else {
212if true {
if !false {
{
::core::panicking::panic_fmt(format_args!("couldn\'t find an existing global-file-id for {0:?}",
source_file));
}
};
};debug_assert!(false, "couldn't find an existing global-file-id for {source_file:?}");
213return;
214 };
215216let llvm_cov::Regions {
217 code_regions,
218 expansion_regions: _, // FIXME(Zalathar): Fill out support for expansion regions
219branch_regions,
220 } = regions;
221222// The global file IDs are stored as `u32` to make FFI easier.
223 // FIXME(Zalathar): Consider giving `newtype_index!` a safe transmute to `&[u32]`.
224let local_file_id = virtual_file_mapping.push(global_file_id.as_u32());
225226for &CodeMapping { coords, counter } in code_mappings {
227let cov_span = coords.make_coverage_span(local_file_id);
228 code_regions.push(ffi::CodeRegion { cov_span, counter });
229 }
230231for &BranchMapping { coords, true_counter, false_counter } in branch_mappings {
232let cov_span = coords.make_coverage_span(local_file_id);
233 branch_regions.push(ffi::BranchRegion { cov_span, true_counter, false_counter });
234 }
235}
236237/// Generates and emits the covfun record for this function, which
238/// contains the function's coverage mapping data. The record is emitted
239/// as a global variable in the `__llvm_covfun` section.
240pub(crate) fn emit_covfun_record<'tcx>(
241 cx: &mut CodegenCx<'_, 'tcx>,
242 global_file_table: &GlobalFileTable,
243 covfun: &CovfunRecord<'tcx>,
244) {
245let &CovfunRecord {
246 _instance,
247 mangled_function_name,
248 source_hash,
249 is_used,
250ref expressions,
251ref mappings,
252 } = covfun;
253254let mut regions = llvm_cov::Regions::default();
255let mut virtual_file_mapping = IndexVec::new();
256fill_region_tables(global_file_table, mappings, &mut virtual_file_mapping, &mut regions);
257258if regions.has_no_regions() {
259if true {
if !false {
{
::core::panicking::panic_fmt(format_args!("mappings should have produced at least one region: {0:#?}",
mappings));
}
};
};debug_assert!(false, "mappings should have produced at least one region: {mappings:#?}");
260return;
261 }
262263// Encode the function's coverage mappings into a buffer.
264let coverage_mapping_buffer = llvm_cov::write_function_mappings_to_buffer(
265&virtual_file_mapping.raw,
266expressions,
267®ions,
268 );
269270// A covfun record consists of four target-endian integers, followed by the
271 // encoded mapping data in bytes. Note that the length field is 32 bits.
272 // <https://llvm.org/docs/CoverageMappingFormat.html#llvm-ir-representation>
273 // See also `src/llvm-project/clang/lib/CodeGen/CoverageMappingGen.cpp` and
274 // `COVMAP_V3` in `src/llvm-project/llvm/include/llvm/ProfileData/InstrProfData.inc`.
275let func_name_hash = llvm_cov::hash_bytes(mangled_function_name.as_bytes());
276let covfun_record = cx.const_struct(
277&[
278cx.const_u64(func_name_hash),
279cx.const_u32(coverage_mapping_buffer.len() as u32),
280cx.const_u64(source_hash),
281cx.const_u64(global_file_table.filenames_hash),
282cx.const_bytes(&coverage_mapping_buffer),
283 ],
284// This struct needs to be packed, so that the 32-bit length field
285 // doesn't have unexpected padding.
286true,
287 );
288289// Choose a variable name to hold this function's covfun data.
290 // Functions that are used have a suffix ("u") to distinguish them from
291 // unused copies of the same function (from different CGUs), so that if a
292 // linker sees both it won't discard the used copy's data.
293let u = if is_used { "u" } else { "" };
294let covfun_var_name = CString::new(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("__covrec_{0:X}{1}", func_name_hash,
u))
})format!("__covrec_{func_name_hash:X}{u}")).unwrap();
295{
use ::tracing::__macro_support::Callsite as _;
static __CALLSITE: ::tracing::callsite::DefaultCallsite =
{
static META: ::tracing::Metadata<'static> =
{
::tracing_core::metadata::Metadata::new("event /rustc-dev/809936eac66c547a5127ce1da805f0d3a6789b98/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs:295",
"rustc_codegen_llvm::coverageinfo::mapgen::covfun",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("/rustc-dev/809936eac66c547a5127ce1da805f0d3a6789b98/compiler/rustc_codegen_llvm/src/coverageinfo/mapgen/covfun.rs"),
::tracing_core::__macro_support::Option::Some(295u32),
::tracing_core::__macro_support::Option::Some("rustc_codegen_llvm::coverageinfo::mapgen::covfun"),
::tracing_core::field::FieldSet::new(&["message"],
::tracing_core::callsite::Identifier(&__CALLSITE)),
::tracing::metadata::Kind::EVENT)
};
::tracing::callsite::DefaultCallsite::new(&META)
};
let enabled =
::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
&&
::tracing::Level::DEBUG <=
::tracing::level_filters::LevelFilter::current() &&
{
let interest = __CALLSITE.interest();
!interest.is_never() &&
::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
interest)
};
if enabled {
(|value_set: ::tracing::field::ValueSet|
{
let meta = __CALLSITE.metadata();
::tracing::Event::dispatch(meta, &value_set);
;
})({
#[allow(unused_imports)]
use ::tracing::field::{debug, display, Value};
__CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("function record var name: {0:?}",
covfun_var_name) as &dyn ::tracing::field::Value))])
});
} else { ; }
};debug!("function record var name: {covfun_var_name:?}");
296297let covfun_global = llvm::add_global(cx.llmod, cx.val_ty(covfun_record), &covfun_var_name);
298 llvm::set_initializer(covfun_global, covfun_record);
299 llvm::set_global_constant(covfun_global, true);
300 llvm::set_linkage(covfun_global, llvm::Linkage::LinkOnceODRLinkage);
301 llvm::set_visibility(covfun_global, llvm::Visibility::Hidden);
302 llvm::set_section(covfun_global, cx.covfun_section_name());
303// LLVM's coverage mapping format specifies 8-byte alignment for items in this section.
304 // <https://llvm.org/docs/CoverageMappingFormat.html>
305llvm::set_alignment(covfun_global, Align::EIGHT);
306if cx.target_spec().supports_comdat() {
307 llvm::set_comdat(cx.llmod, covfun_global, &covfun_var_name);
308 }
309310cx.add_used_global(covfun_global);
311}