@@ -11,6 +11,7 @@ use rustc_abi::Align;
11
11
use rustc_codegen_ssa:: traits:: {
12
12
BaseTypeCodegenMethods as _, ConstCodegenMethods , StaticCodegenMethods ,
13
13
} ;
14
+ use rustc_index:: IndexVec ;
14
15
use rustc_middle:: mir:: coverage:: {
15
16
BasicCoverageBlock , CovTerm , CoverageIdsInfo , Expression , FunctionCoverageInfo , Mapping ,
16
17
MappingKind , Op ,
@@ -104,6 +105,16 @@ fn fill_region_tables<'tcx>(
104
105
ids_info : & ' tcx CoverageIdsInfo ,
105
106
covfun : & mut CovfunRecord < ' tcx > ,
106
107
) {
108
+ // If this function is unused, replace all counters with zero.
109
+ let counter_for_bcb = |bcb : BasicCoverageBlock | -> ffi:: Counter {
110
+ let term = if covfun. is_used {
111
+ ids_info. term_for_bcb [ bcb] . expect ( "every BCB in a mapping was given a term" )
112
+ } else {
113
+ CovTerm :: Zero
114
+ } ;
115
+ ffi:: Counter :: from_term ( term)
116
+ } ;
117
+
107
118
// Currently a function's mappings must all be in the same file, so use the
108
119
// first mapping's span to determine the file.
109
120
let source_map = tcx. sess . source_map ( ) ;
@@ -115,6 +126,12 @@ fn fill_region_tables<'tcx>(
115
126
116
127
let local_file_id = covfun. virtual_file_mapping . push_file ( & source_file) ;
117
128
129
+ // If this testing flag is set, add an extra unused entry to the local
130
+ // file table, to help test the code for detecting unused file IDs.
131
+ if tcx. sess . coverage_inject_unused_local_file ( ) {
132
+ covfun. virtual_file_mapping . push_file ( & source_file) ;
133
+ }
134
+
118
135
// In rare cases, _all_ of a function's spans are discarded, and coverage
119
136
// codegen needs to handle that gracefully to avoid #133606.
120
137
// It's hard for tests to trigger this organically, so instead we set
@@ -135,16 +152,6 @@ fn fill_region_tables<'tcx>(
135
152
// For each counter/region pair in this function+file, convert it to a
136
153
// form suitable for FFI.
137
154
for & Mapping { ref kind, span } in & fn_cov_info. mappings {
138
- // If this function is unused, replace all counters with zero.
139
- let counter_for_bcb = |bcb : BasicCoverageBlock | -> ffi:: Counter {
140
- let term = if covfun. is_used {
141
- ids_info. term_for_bcb [ bcb] . expect ( "every BCB in a mapping was given a term" )
142
- } else {
143
- CovTerm :: Zero
144
- } ;
145
- ffi:: Counter :: from_term ( term)
146
- } ;
147
-
148
155
let Some ( coords) = make_coords ( span) else { continue } ;
149
156
let cov_span = coords. make_coverage_span ( local_file_id) ;
150
157
@@ -177,6 +184,19 @@ fn fill_region_tables<'tcx>(
177
184
}
178
185
}
179
186
187
+ /// LLVM requires all local file IDs to have at least one mapping region.
188
+ /// If that's not the case, skip this function, to avoid an assertion failure
189
+ /// (or worse) in LLVM.
190
+ fn check_local_file_table ( covfun : & CovfunRecord < ' _ > ) -> bool {
191
+ let mut local_file_id_seen =
192
+ IndexVec :: < u32 , _ > :: from_elem_n ( false , covfun. virtual_file_mapping . local_file_table . len ( ) ) ;
193
+ for cov_span in covfun. regions . all_cov_spans ( ) {
194
+ local_file_id_seen[ cov_span. file_id ] = true ;
195
+ }
196
+
197
+ local_file_id_seen. into_iter ( ) . all ( |seen| seen)
198
+ }
199
+
180
200
/// Generates the contents of the covfun record for this function, which
181
201
/// contains the function's coverage mapping data. The record is then stored
182
202
/// as a global variable in the `__llvm_covfun` section.
@@ -185,6 +205,10 @@ pub(crate) fn generate_covfun_record<'tcx>(
185
205
global_file_table : & GlobalFileTable ,
186
206
covfun : & CovfunRecord < ' tcx > ,
187
207
) {
208
+ if !check_local_file_table ( covfun) {
209
+ return ;
210
+ }
211
+
188
212
let & CovfunRecord {
189
213
mangled_function_name,
190
214
source_hash,
0 commit comments