Skip to content

Commit 7c4ac71

Browse files
committed
coverage: Extract function metadata handling to a covfun submodule
1 parent 33c245b commit 7c4ac71

File tree

2 files changed

+206
-166
lines changed

2 files changed

+206
-166
lines changed

compiler/rustc_codegen_llvm/src/coverageinfo/mapgen.rs

+8-166
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use std::ffi::CString;
21
use std::iter;
32

43
use itertools::Itertools as _;
@@ -9,21 +8,21 @@ use rustc_codegen_ssa::traits::{
98
use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
109
use rustc_hir::def_id::{DefId, LocalDefId};
1110
use rustc_index::IndexVec;
12-
use rustc_middle::mir::coverage::MappingKind;
1311
use rustc_middle::ty::{self, TyCtxt};
1412
use rustc_middle::{bug, mir};
1513
use rustc_session::RemapFileNameExt;
1614
use rustc_session::config::RemapPathScopeComponents;
1715
use rustc_span::def_id::DefIdSet;
1816
use rustc_span::{Span, Symbol};
19-
use rustc_target::spec::HasTargetSpec;
2017
use tracing::debug;
2118

2219
use crate::common::CodegenCx;
20+
use crate::coverageinfo::llvm_cov;
2321
use crate::coverageinfo::map_data::FunctionCoverage;
24-
use crate::coverageinfo::{ffi, llvm_cov};
2522
use crate::llvm;
2623

24+
mod covfun;
25+
2726
/// Generates and exports the coverage map, which is embedded in special
2827
/// linker sections in the final binary.
2928
///
@@ -88,38 +87,13 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
8887

8988
// Encode coverage mappings and generate function records
9089
for (instance, function_coverage) in function_coverage_map {
91-
debug!("Generate function coverage for {}, {:?}", cx.codegen_unit.name(), instance);
92-
93-
let mangled_function_name = tcx.symbol_name(instance).name;
94-
let source_hash = function_coverage.source_hash();
95-
let is_used = function_coverage.is_used();
96-
97-
let coverage_mapping_buffer =
98-
encode_mappings_for_function(tcx, &global_file_table, &function_coverage);
99-
100-
if coverage_mapping_buffer.is_empty() {
101-
if function_coverage.is_used() {
102-
bug!(
103-
"A used function should have had coverage mapping data but did not: {}",
104-
mangled_function_name
105-
);
106-
} else {
107-
debug!("unused function had no coverage mapping data: {}", mangled_function_name);
108-
continue;
109-
}
110-
}
111-
112-
if !is_used {
113-
unused_function_names.push(mangled_function_name);
114-
}
115-
116-
generate_covfun_record(
90+
covfun::prepare_and_generate_covfun_record(
11791
cx,
118-
mangled_function_name,
119-
source_hash,
92+
&global_file_table,
12093
filenames_ref,
121-
coverage_mapping_buffer,
122-
is_used,
94+
&mut unused_function_names,
95+
instance,
96+
&function_coverage,
12397
);
12498
}
12599

@@ -236,83 +210,6 @@ fn span_file_name(tcx: TyCtxt<'_>, span: Span) -> Symbol {
236210
Symbol::intern(&name)
237211
}
238212

239-
/// Using the expressions and counter regions collected for a single function,
240-
/// generate the variable-sized payload of its corresponding `__llvm_covfun`
241-
/// entry. The payload is returned as a vector of bytes.
242-
///
243-
/// Newly-encountered filenames will be added to the global file table.
244-
fn encode_mappings_for_function(
245-
tcx: TyCtxt<'_>,
246-
global_file_table: &GlobalFileTable,
247-
function_coverage: &FunctionCoverage<'_>,
248-
) -> Vec<u8> {
249-
let counter_regions = function_coverage.counter_regions();
250-
if counter_regions.is_empty() {
251-
return Vec::new();
252-
}
253-
254-
let expressions = function_coverage.counter_expressions().collect::<Vec<_>>();
255-
256-
let mut virtual_file_mapping = VirtualFileMapping::default();
257-
let mut code_regions = vec![];
258-
let mut branch_regions = vec![];
259-
let mut mcdc_branch_regions = vec![];
260-
let mut mcdc_decision_regions = vec![];
261-
262-
// Currently a function's mappings must all be in the same file as its body span.
263-
let file_name = span_file_name(tcx, function_coverage.function_coverage_info.body_span);
264-
265-
// Look up the global file ID for that filename.
266-
let global_file_id = global_file_table.global_file_id_for_file_name(file_name);
267-
268-
// Associate that global file ID with a local file ID for this function.
269-
let local_file_id = virtual_file_mapping.local_id_for_global(global_file_id);
270-
debug!(" file id: {local_file_id:?} => {global_file_id:?} = '{file_name:?}'");
271-
272-
// For each counter/region pair in this function+file, convert it to a
273-
// form suitable for FFI.
274-
for (mapping_kind, region) in counter_regions {
275-
debug!("Adding counter {mapping_kind:?} to map for {region:?}");
276-
let span = ffi::CoverageSpan::from_source_region(local_file_id, region);
277-
match mapping_kind {
278-
MappingKind::Code(term) => {
279-
code_regions.push(ffi::CodeRegion { span, counter: ffi::Counter::from_term(term) });
280-
}
281-
MappingKind::Branch { true_term, false_term } => {
282-
branch_regions.push(ffi::BranchRegion {
283-
span,
284-
true_counter: ffi::Counter::from_term(true_term),
285-
false_counter: ffi::Counter::from_term(false_term),
286-
});
287-
}
288-
MappingKind::MCDCBranch { true_term, false_term, mcdc_params } => {
289-
mcdc_branch_regions.push(ffi::MCDCBranchRegion {
290-
span,
291-
true_counter: ffi::Counter::from_term(true_term),
292-
false_counter: ffi::Counter::from_term(false_term),
293-
mcdc_branch_params: ffi::mcdc::BranchParameters::from(mcdc_params),
294-
});
295-
}
296-
MappingKind::MCDCDecision(mcdc_decision_params) => {
297-
mcdc_decision_regions.push(ffi::MCDCDecisionRegion {
298-
span,
299-
mcdc_decision_params: ffi::mcdc::DecisionParameters::from(mcdc_decision_params),
300-
});
301-
}
302-
}
303-
}
304-
305-
// Encode the function's coverage mappings into a buffer.
306-
llvm_cov::write_function_mappings_to_buffer(
307-
&virtual_file_mapping.into_vec(),
308-
&expressions,
309-
&code_regions,
310-
&branch_regions,
311-
&mcdc_branch_regions,
312-
&mcdc_decision_regions,
313-
)
314-
}
315-
316213
/// Generates the contents of the covmap record for this CGU, which mostly
317214
/// consists of a header and a list of filenames. The record is then stored
318215
/// as a global variable in the `__llvm_covmap` section.
@@ -350,61 +247,6 @@ fn generate_covmap_record<'ll>(
350247
cx.add_used_global(llglobal);
351248
}
352249

353-
/// Generates the contents of the covfun record for this function, which
354-
/// contains the function's coverage mapping data. The record is then stored
355-
/// as a global variable in the `__llvm_covfun` section.
356-
fn generate_covfun_record(
357-
cx: &CodegenCx<'_, '_>,
358-
mangled_function_name: &str,
359-
source_hash: u64,
360-
filenames_ref: u64,
361-
coverage_mapping_buffer: Vec<u8>,
362-
is_used: bool,
363-
) {
364-
// Concatenate the encoded coverage mappings
365-
let coverage_mapping_size = coverage_mapping_buffer.len();
366-
let coverage_mapping_val = cx.const_bytes(&coverage_mapping_buffer);
367-
368-
let func_name_hash = llvm_cov::hash_bytes(mangled_function_name.as_bytes());
369-
let func_name_hash_val = cx.const_u64(func_name_hash);
370-
let coverage_mapping_size_val = cx.const_u32(coverage_mapping_size as u32);
371-
let source_hash_val = cx.const_u64(source_hash);
372-
let filenames_ref_val = cx.const_u64(filenames_ref);
373-
let func_record_val = cx.const_struct(
374-
&[
375-
func_name_hash_val,
376-
coverage_mapping_size_val,
377-
source_hash_val,
378-
filenames_ref_val,
379-
coverage_mapping_val,
380-
],
381-
/*packed=*/ true,
382-
);
383-
384-
// Choose a variable name to hold this function's covfun data.
385-
// Functions that are used have a suffix ("u") to distinguish them from
386-
// unused copies of the same function (from different CGUs), so that if a
387-
// linker sees both it won't discard the used copy's data.
388-
let func_record_var_name =
389-
CString::new(format!("__covrec_{:X}{}", func_name_hash, if is_used { "u" } else { "" }))
390-
.unwrap();
391-
debug!("function record var name: {:?}", func_record_var_name);
392-
393-
let llglobal = llvm::add_global(cx.llmod, cx.val_ty(func_record_val), &func_record_var_name);
394-
llvm::set_initializer(llglobal, func_record_val);
395-
llvm::set_global_constant(llglobal, true);
396-
llvm::set_linkage(llglobal, llvm::Linkage::LinkOnceODRLinkage);
397-
llvm::set_visibility(llglobal, llvm::Visibility::Hidden);
398-
llvm::set_section(llglobal, cx.covfun_section_name());
399-
// LLVM's coverage mapping format specifies 8-byte alignment for items in this section.
400-
// <https://llvm.org/docs/CoverageMappingFormat.html>
401-
llvm::set_alignment(llglobal, Align::EIGHT);
402-
if cx.target_spec().supports_comdat() {
403-
llvm::set_comdat(cx.llmod, llglobal, &func_record_var_name);
404-
}
405-
cx.add_used_global(llglobal);
406-
}
407-
408250
/// Each CGU will normally only emit coverage metadata for the functions that it actually generates.
409251
/// But since we don't want unused functions to disappear from coverage reports, we also scan for
410252
/// functions that were instrumented but are not participating in codegen.

0 commit comments

Comments
 (0)