1
1
use crate :: coverageinfo:: ffi:: { Counter , CounterExpression , ExprKind } ;
2
2
3
3
use rustc_data_structures:: fx:: FxIndexSet ;
4
- use rustc_index:: { IndexSlice , IndexVec } ;
5
- use rustc_middle:: bug;
6
- use rustc_middle:: mir:: coverage:: {
7
- CodeRegion , CounterId , ExpressionId , MappedExpressionIndex , Op , Operand ,
8
- } ;
4
+ use rustc_index:: IndexVec ;
5
+ use rustc_middle:: mir:: coverage:: { CodeRegion , CounterId , ExpressionId , Op , Operand } ;
9
6
use rustc_middle:: ty:: Instance ;
10
7
use rustc_middle:: ty:: TyCtxt ;
11
8
@@ -195,8 +192,14 @@ impl<'tcx> FunctionCoverage<'tcx> {
195
192
self . instance
196
193
) ;
197
194
195
+ let counter_expressions = self . counter_expressions ( ) ;
196
+ // Expression IDs are indices into `self.expressions`, and on the LLVM
197
+ // side they will be treated as indices into `counter_expressions`, so
198
+ // the two vectors should correspond 1:1.
199
+ assert_eq ! ( self . expressions. len( ) , counter_expressions. len( ) ) ;
200
+
198
201
let counter_regions = self . counter_regions ( ) ;
199
- let ( counter_expressions , expression_regions) = self . expressions_with_regions ( ) ;
202
+ let expression_regions = self . expression_regions ( ) ;
200
203
let unreachable_regions = self . unreachable_regions ( ) ;
201
204
202
205
let counter_regions =
@@ -212,146 +215,47 @@ impl<'tcx> FunctionCoverage<'tcx> {
212
215
} )
213
216
}
214
217
215
- fn expressions_with_regions (
216
- & self ,
217
- ) -> ( Vec < CounterExpression > , impl Iterator < Item = ( Counter , & CodeRegion ) > ) {
218
- let mut counter_expressions = Vec :: with_capacity ( self . expressions . len ( ) ) ;
219
- let mut expression_regions = Vec :: with_capacity ( self . expressions . len ( ) ) ;
220
- let mut new_indexes = IndexVec :: from_elem_n ( None , self . expressions . len ( ) ) ;
221
-
222
- // This closure converts any `Expression` operand (`lhs` or `rhs` of the `Op::Add` or
223
- // `Op::Subtract` operation) into its native `llvm::coverage::Counter::CounterKind` type
224
- // and value.
225
- //
226
- // Expressions will be returned from this function in a sequential vector (array) of
227
- // `CounterExpression`, so the expression IDs must be mapped from their original,
228
- // potentially sparse set of indexes.
229
- //
230
- // An `Expression` as an operand will have already been encountered as an `Expression` with
231
- // operands, so its new_index will already have been generated (as a 1-up index value).
232
- // (If an `Expression` as an operand does not have a corresponding new_index, it was
233
- // probably optimized out, after the expression was injected into the MIR, so it will
234
- // get a `CounterKind::Zero` instead.)
235
- //
236
- // In other words, an `Expression`s at any given index can include other expressions as
237
- // operands, but expression operands can only come from the subset of expressions having
238
- // `expression_index`s lower than the referencing `Expression`. Therefore, it is
239
- // reasonable to look up the new index of an expression operand while the `new_indexes`
240
- // vector is only complete up to the current `ExpressionIndex`.
241
- type NewIndexes = IndexSlice < ExpressionId , Option < MappedExpressionIndex > > ;
242
- let id_to_counter = |new_indexes : & NewIndexes , operand : Operand | match operand {
243
- Operand :: Zero => Some ( Counter :: ZERO ) ,
244
- Operand :: Counter ( id) => Some ( Counter :: counter_value_reference ( id) ) ,
245
- Operand :: Expression ( id) => {
246
- self . expressions
247
- . get ( id)
248
- . expect ( "expression id is out of range" )
249
- . as_ref ( )
250
- // If an expression was optimized out, assume it would have produced a count
251
- // of zero. This ensures that expressions dependent on optimized-out
252
- // expressions are still valid.
253
- . map_or ( Some ( Counter :: ZERO ) , |_| new_indexes[ id] . map ( Counter :: expression) )
254
- }
255
- } ;
256
-
257
- for ( original_index, expression) in
258
- self . expressions . iter_enumerated ( ) . filter_map ( |( original_index, entry) | {
259
- // Option::map() will return None to filter out missing expressions. This may happen
260
- // if, for example, a MIR-instrumented expression is removed during an optimization.
261
- entry. as_ref ( ) . map ( |expression| ( original_index, expression) )
262
- } )
263
- {
264
- let optional_region = & expression. region ;
265
- let Expression { lhs, op, rhs, .. } = * expression;
218
+ /// Convert this function's coverage expression data into a form that can be
219
+ /// passed through FFI to LLVM.
220
+ fn counter_expressions ( & self ) -> Vec < CounterExpression > {
221
+ // We know that LLVM will optimize out any unused expressions before
222
+ // producing the final coverage map, so there's no need to do the same
223
+ // thing on the Rust side unless we're confident we can do much better.
224
+ // (See `CounterExpressionsMinimizer` in `CoverageMappingWriter.cpp`.)
266
225
267
- if let Some ( Some ( ( lhs_counter, mut rhs_counter) ) ) = id_to_counter ( & new_indexes, lhs)
268
- . map ( |lhs_counter| {
269
- id_to_counter ( & new_indexes, rhs) . map ( |rhs_counter| ( lhs_counter, rhs_counter) )
270
- } )
271
- {
272
- if lhs_counter. is_zero ( ) && op. is_subtract ( ) {
273
- // The left side of a subtraction was probably optimized out. As an example,
274
- // a branch condition might be evaluated as a constant expression, and the
275
- // branch could be removed, dropping unused counters in the process.
276
- //
277
- // Since counters are unsigned, we must assume the result of the expression
278
- // can be no more and no less than zero. An expression known to evaluate to zero
279
- // does not need to be added to the coverage map.
280
- //
281
- // Coverage test `loops_branches.rs` includes multiple variations of branches
282
- // based on constant conditional (literal `true` or `false`), and demonstrates
283
- // that the expected counts are still correct.
284
- debug ! (
285
- "Expression subtracts from zero (assume unreachable): \
286
- original_index={:?}, lhs={:?}, op={:?}, rhs={:?}, region={:?}",
287
- original_index, lhs, op, rhs, optional_region,
288
- ) ;
289
- rhs_counter = Counter :: ZERO ;
226
+ self . expressions
227
+ . iter ( )
228
+ . map ( |expression| match expression {
229
+ None => {
230
+ // This expression ID was allocated, but we never saw the
231
+ // actual expression, so it must have been optimized out.
232
+ // Replace it with a dummy expression, and let LLVM take
233
+ // care of omitting it from the expression list.
234
+ CounterExpression :: DUMMY
290
235
}
291
- debug_assert ! (
292
- lhs_counter. is_zero( )
293
- // Note: with `as usize` the ID _could_ overflow/wrap if `usize = u16`
294
- || ( ( lhs_counter. zero_based_id( ) as usize )
295
- <= usize :: max( self . counters. len( ) , self . expressions. len( ) ) ) ,
296
- "lhs id={} > both counters.len()={} and expressions.len()={}
297
- ({:?} {:?} {:?})" ,
298
- lhs_counter. zero_based_id( ) ,
299
- self . counters. len( ) ,
300
- self . expressions. len( ) ,
301
- lhs_counter,
302
- op,
303
- rhs_counter,
304
- ) ;
305
-
306
- debug_assert ! (
307
- rhs_counter. is_zero( )
308
- // Note: with `as usize` the ID _could_ overflow/wrap if `usize = u16`
309
- || ( ( rhs_counter. zero_based_id( ) as usize )
310
- <= usize :: max( self . counters. len( ) , self . expressions. len( ) ) ) ,
311
- "rhs id={} > both counters.len()={} and expressions.len()={}
312
- ({:?} {:?} {:?})" ,
313
- rhs_counter. zero_based_id( ) ,
314
- self . counters. len( ) ,
315
- self . expressions. len( ) ,
316
- lhs_counter,
317
- op,
318
- rhs_counter,
319
- ) ;
320
-
321
- // Both operands exist. `Expression` operands exist in `self.expressions` and have
322
- // been assigned a `new_index`.
323
- let mapped_expression_index =
324
- MappedExpressionIndex :: from ( counter_expressions. len ( ) ) ;
325
- let expression = CounterExpression :: new (
326
- lhs_counter,
236
+ & Some ( Expression { lhs, op, rhs, .. } ) => CounterExpression :: new (
237
+ Counter :: from_operand ( lhs) ,
327
238
match op {
328
239
Op :: Add => ExprKind :: Add ,
329
240
Op :: Subtract => ExprKind :: Subtract ,
330
241
} ,
331
- rhs_counter,
332
- ) ;
333
- debug ! (
334
- "Adding expression {:?} = {:?}, region: {:?}" ,
335
- mapped_expression_index, expression, optional_region
336
- ) ;
337
- counter_expressions. push ( expression) ;
338
- new_indexes[ original_index] = Some ( mapped_expression_index) ;
339
- if let Some ( region) = optional_region {
340
- expression_regions. push ( ( Counter :: expression ( mapped_expression_index) , region) ) ;
341
- }
342
- } else {
343
- bug ! (
344
- "expression has one or more missing operands \
345
- original_index={:?}, lhs={:?}, op={:?}, rhs={:?}, region={:?}",
346
- original_index,
347
- lhs,
348
- op,
349
- rhs,
350
- optional_region,
351
- ) ;
352
- }
353
- }
354
- ( counter_expressions, expression_regions. into_iter ( ) )
242
+ Counter :: from_operand ( rhs) ,
243
+ ) ,
244
+ } )
245
+ . collect :: < Vec < _ > > ( )
246
+ }
247
+
248
+ fn expression_regions ( & self ) -> Vec < ( Counter , & CodeRegion ) > {
249
+ // Find all of the expression IDs that weren't optimized out AND have
250
+ // an attached code region, and return the corresponding mapping as a
251
+ // counter/region pair.
252
+ self . expressions
253
+ . iter_enumerated ( )
254
+ . filter_map ( |( id, expression) | {
255
+ let code_region = expression. as_ref ( ) ?. region . as_ref ( ) ?;
256
+ Some ( ( Counter :: expression ( id) , code_region) )
257
+ } )
258
+ . collect :: < Vec < _ > > ( )
355
259
}
356
260
357
261
fn unreachable_regions ( & self ) -> impl Iterator < Item = ( Counter , & CodeRegion ) > {
0 commit comments