1- use std:: ffi:: CString ;
21use std:: iter;
32
43use itertools:: Itertools as _;
@@ -9,21 +8,22 @@ use rustc_codegen_ssa::traits::{
98use rustc_data_structures:: fx:: { FxHashSet , FxIndexMap , FxIndexSet } ;
109use rustc_hir:: def_id:: { DefId , LocalDefId } ;
1110use rustc_index:: IndexVec ;
12- use rustc_middle:: mir:: coverage:: MappingKind ;
1311use rustc_middle:: ty:: { self , TyCtxt } ;
1412use rustc_middle:: { bug, mir} ;
1513use rustc_session:: RemapFileNameExt ;
1614use rustc_session:: config:: RemapPathScopeComponents ;
1715use rustc_span:: def_id:: DefIdSet ;
1816use rustc_span:: { Span , Symbol } ;
19- use rustc_target:: spec:: HasTargetSpec ;
2017use tracing:: debug;
2118
2219use crate :: common:: CodegenCx ;
20+ use crate :: coverageinfo:: llvm_cov;
2321use crate :: coverageinfo:: map_data:: FunctionCoverage ;
24- use crate :: coverageinfo:: { ffi , llvm_cov } ;
22+ use crate :: coverageinfo:: mapgen :: covfun :: prepare_covfun_record ;
2523use crate :: llvm;
2624
25+ mod covfun;
26+
2727/// Generates and exports the coverage map, which is embedded in special
2828/// linker sections in the final binary.
2929///
@@ -80,47 +80,28 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
8080 let filenames_val = cx. const_bytes ( & filenames_buffer) ;
8181 let filenames_ref = llvm_cov:: hash_bytes ( & filenames_buffer) ;
8282
83- // Generate the coverage map header, which contains the filenames used by
84- // this CGU's coverage mappings, and store it in a well-known global.
85- generate_covmap_record ( cx, covmap_version, filenames_size, filenames_val) ;
86-
8783 let mut unused_function_names = Vec :: new ( ) ;
8884
89- // Encode coverage mappings and generate function records
90- 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- }
85+ let covfun_records = function_coverage_map
86+ . into_iter ( )
87+ . filter_map ( |( instance, function_coverage) | {
88+ prepare_covfun_record ( tcx, & global_file_table, instance, & function_coverage)
89+ } )
90+ . collect :: < Vec < _ > > ( ) ;
91+
92+ // If there are no covfun records for this CGU, don't generate a covmap record.
93+ // Emitting a covmap record without any covfun records causes `llvm-cov` to
94+ // fail when generating coverage reports, and if there are no covfun records
95+ // then the covmap record isn't useful anyway.
96+ // This should prevent a repeat of <https://github.com/rust-lang/rust/issues/133606>.
97+ if covfun_records. is_empty ( ) {
98+ return ;
99+ }
111100
112- if !is_used {
113- unused_function_names. push ( mangled_function_name) ;
114- }
101+ for covfun in & covfun_records {
102+ unused_function_names. extend ( covfun. mangled_function_name_if_unused ( ) ) ;
115103
116- generate_covfun_record (
117- cx,
118- mangled_function_name,
119- source_hash,
120- filenames_ref,
121- coverage_mapping_buffer,
122- is_used,
123- ) ;
104+ covfun:: generate_covfun_record ( cx, filenames_ref, covfun)
124105 }
125106
126107 // For unused functions, we need to take their mangled names and store them
@@ -141,6 +122,11 @@ pub(crate) fn finalize(cx: &CodegenCx<'_, '_>) {
141122 llvm:: set_linkage ( array, llvm:: Linkage :: InternalLinkage ) ;
142123 llvm:: set_initializer ( array, initializer) ;
143124 }
125+
126+ // Generate the coverage map header, which contains the filenames used by
127+ // this CGU's coverage mappings, and store it in a well-known global.
128+ // (This is skipped if we returned early due to having no covfun records.)
129+ generate_covmap_record ( cx, covmap_version, filenames_size, filenames_val) ;
144130}
145131
146132/// Maps "global" (per-CGU) file ID numbers to their underlying filenames.
@@ -208,7 +194,7 @@ rustc_index::newtype_index! {
208194
209195/// Holds a mapping from "local" (per-function) file IDs to "global" (per-CGU)
210196/// file IDs.
211- #[ derive( Default ) ]
197+ #[ derive( Debug , Default ) ]
212198struct VirtualFileMapping {
213199 local_to_global : IndexVec < LocalFileId , GlobalFileId > ,
214200 global_to_local : FxIndexMap < GlobalFileId , LocalFileId > ,
@@ -222,10 +208,10 @@ impl VirtualFileMapping {
222208 . or_insert_with ( || self . local_to_global . push ( global_file_id) )
223209 }
224210
225- fn into_vec ( self ) -> Vec < u32 > {
226- // This conversion should be optimized away to ~zero overhead.
227- // In any case, it's probably not hot enough to worry about .
228- self . local_to_global . into_iter ( ) . map ( |global| global . as_u32 ( ) ) . collect ( )
211+ fn to_vec ( & self ) -> Vec < u32 > {
212+ // This clone could be avoided by transmuting `&[GlobalFileId]` to `&[u32]`,
213+ // but it isn't hot or expensive enough to justify the extra unsafety .
214+ self . local_to_global . iter ( ) . map ( |& global| GlobalFileId :: as_u32 ( global ) ) . collect ( )
229215 }
230216}
231217
@@ -236,83 +222,6 @@ fn span_file_name(tcx: TyCtxt<'_>, span: Span) -> Symbol {
236222 Symbol :: intern ( & name)
237223}
238224
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-
316225/// Generates the contents of the covmap record for this CGU, which mostly
317226/// consists of a header and a list of filenames. The record is then stored
318227/// as a global variable in the `__llvm_covmap` section.
@@ -350,61 +259,6 @@ fn generate_covmap_record<'ll>(
350259 cx. add_used_global ( llglobal) ;
351260}
352261
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-
408262/// Each CGU will normally only emit coverage metadata for the functions that it actually generates.
409263/// But since we don't want unused functions to disappear from coverage reports, we also scan for
410264/// functions that were instrumented but are not participating in codegen.
0 commit comments