@@ -2,18 +2,18 @@ pub use super::ffi::*;
2
2
3
3
use rustc_index:: vec:: IndexVec ;
4
4
use rustc_middle:: mir:: coverage:: {
5
- CodeRegion , CounterValueReference , ExpressionOperandId , InjectedExpressionIndex ,
6
- MappedExpressionIndex , Op ,
5
+ CodeRegion , CounterValueReference , ExpressionOperandId , InjectedExpressionId ,
6
+ InjectedExpressionIndex , MappedExpressionIndex , Op ,
7
7
} ;
8
8
use rustc_middle:: ty:: Instance ;
9
9
use rustc_middle:: ty:: TyCtxt ;
10
10
11
11
#[ derive( Clone , Debug ) ]
12
- pub struct ExpressionRegion {
12
+ pub struct Expression {
13
13
lhs : ExpressionOperandId ,
14
14
op : Op ,
15
15
rhs : ExpressionOperandId ,
16
- region : CodeRegion ,
16
+ region : Option < CodeRegion > ,
17
17
}
18
18
19
19
/// Collects all of the coverage regions associated with (a) injected counters, (b) counter
@@ -28,24 +28,43 @@ pub struct ExpressionRegion {
28
28
/// only whitespace or comments). According to LLVM Code Coverage Mapping documentation, "A count
29
29
/// for a gap area is only used as the line execution count if there are no other regions on a
30
30
/// line."
31
- pub struct FunctionCoverage {
31
+ pub struct FunctionCoverage < ' tcx > {
32
+ instance : Instance < ' tcx > ,
32
33
source_hash : u64 ,
33
34
counters : IndexVec < CounterValueReference , Option < CodeRegion > > ,
34
- expressions : IndexVec < InjectedExpressionIndex , Option < ExpressionRegion > > ,
35
+ expressions : IndexVec < InjectedExpressionIndex , Option < Expression > > ,
35
36
unreachable_regions : Vec < CodeRegion > ,
36
37
}
37
38
38
- impl FunctionCoverage {
39
- pub fn new < ' tcx > ( tcx : TyCtxt < ' tcx > , instance : Instance < ' tcx > ) -> Self {
39
+ impl < ' tcx > FunctionCoverage < ' tcx > {
40
+ pub fn new ( tcx : TyCtxt < ' tcx > , instance : Instance < ' tcx > ) -> Self {
40
41
let coverageinfo = tcx. coverageinfo ( instance. def_id ( ) ) ;
42
+ debug ! (
43
+ "FunctionCoverage::new(instance={:?}) has coverageinfo={:?}" ,
44
+ instance, coverageinfo
45
+ ) ;
41
46
Self {
47
+ instance,
42
48
source_hash : 0 , // will be set with the first `add_counter()`
43
49
counters : IndexVec :: from_elem_n ( None , coverageinfo. num_counters as usize ) ,
44
50
expressions : IndexVec :: from_elem_n ( None , coverageinfo. num_expressions as usize ) ,
45
51
unreachable_regions : Vec :: new ( ) ,
46
52
}
47
53
}
48
54
55
+ /// Although every function should have at least one `Counter`, the `Counter` isn't required to
56
+ /// have a `CodeRegion`. (The `CodeRegion` may be associated only with `Expressions`.) This
57
+ /// method supports the ability to ensure the `function_source_hash` is set from `Counters` that
58
+ /// do not trigger the call to `add_counter()` because they don't have an associated
59
+ /// `CodeRegion` to add.
60
+ pub fn set_function_source_hash ( & mut self , source_hash : u64 ) {
61
+ if self . source_hash == 0 {
62
+ self . source_hash = source_hash;
63
+ } else {
64
+ debug_assert_eq ! ( source_hash, self . source_hash) ;
65
+ }
66
+ }
67
+
49
68
/// Adds a code region to be counted by an injected counter intrinsic.
50
69
/// The source_hash (computed during coverage instrumentation) should also be provided, and
51
70
/// should be the same for all counters in a given function.
@@ -74,15 +93,19 @@ impl FunctionCoverage {
74
93
/// counters and expressions have been added.
75
94
pub fn add_counter_expression (
76
95
& mut self ,
77
- expression_id : InjectedExpressionIndex ,
96
+ expression_id : InjectedExpressionId ,
78
97
lhs : ExpressionOperandId ,
79
98
op : Op ,
80
99
rhs : ExpressionOperandId ,
81
- region : CodeRegion ,
100
+ region : Option < CodeRegion > ,
82
101
) {
102
+ debug ! (
103
+ "add_counter_expression({:?}, lhs={:?}, op={:?}, rhs={:?} at {:?}" ,
104
+ expression_id, lhs, op, rhs, region
105
+ ) ;
83
106
let expression_index = self . expression_index ( u32:: from ( expression_id) ) ;
84
107
self . expressions [ expression_index]
85
- . replace ( ExpressionRegion { lhs, op, rhs, region } )
108
+ . replace ( Expression { lhs, op, rhs, region } )
86
109
. expect_none ( "add_counter_expression called with duplicate `id_descending_from_max`" ) ;
87
110
}
88
111
@@ -103,7 +126,11 @@ impl FunctionCoverage {
103
126
pub fn get_expressions_and_counter_regions < ' a > (
104
127
& ' a self ,
105
128
) -> ( Vec < CounterExpression > , impl Iterator < Item = ( Counter , & ' a CodeRegion ) > ) {
106
- assert ! ( self . source_hash != 0 ) ;
129
+ assert ! (
130
+ self . source_hash != 0 ,
131
+ "No counters provided the source_hash for function: {:?}" ,
132
+ self . instance
133
+ ) ;
107
134
108
135
let counter_regions = self . counter_regions ( ) ;
109
136
let ( counter_expressions, expression_regions) = self . expressions_with_regions ( ) ;
@@ -129,54 +156,60 @@ impl FunctionCoverage {
129
156
) -> ( Vec < CounterExpression > , impl Iterator < Item = ( Counter , & ' a CodeRegion ) > ) {
130
157
let mut counter_expressions = Vec :: with_capacity ( self . expressions . len ( ) ) ;
131
158
let mut expression_regions = Vec :: with_capacity ( self . expressions . len ( ) ) ;
132
- let mut new_indexes =
133
- IndexVec :: from_elem_n ( MappedExpressionIndex :: from ( u32:: MAX ) , self . expressions . len ( ) ) ;
134
- // Note, the initial value shouldn't matter since every index in use in `self.expressions`
135
- // will be set, and after that, `new_indexes` will only be accessed using those same
136
- // indexes.
137
-
138
- // Note that an `ExpressionRegion`s at any given index can include other expressions as
159
+ let mut new_indexes = IndexVec :: from_elem_n ( None , self . expressions . len ( ) ) ;
160
+ // Note that an `Expression`s at any given index can include other expressions as
139
161
// operands, but expression operands can only come from the subset of expressions having
140
- // `expression_index`s lower than the referencing `ExpressionRegion `. Therefore, it is
162
+ // `expression_index`s lower than the referencing `Expression `. Therefore, it is
141
163
// reasonable to look up the new index of an expression operand while the `new_indexes`
142
164
// vector is only complete up to the current `ExpressionIndex`.
143
165
let id_to_counter =
144
- |new_indexes : & IndexVec < InjectedExpressionIndex , MappedExpressionIndex > ,
166
+ |new_indexes : & IndexVec < InjectedExpressionIndex , Option < MappedExpressionIndex > > ,
145
167
id : ExpressionOperandId | {
146
168
if id == ExpressionOperandId :: ZERO {
147
169
Some ( Counter :: zero ( ) )
148
170
} else if id. index ( ) < self . counters . len ( ) {
171
+ // Note: Some codegen-injected Counters may be only referenced by `Expression`s,
172
+ // and may not have their own `CodeRegion`s,
149
173
let index = CounterValueReference :: from ( id. index ( ) ) ;
150
- self . counters
151
- . get ( index)
152
- . unwrap ( ) // pre-validated
153
- . as_ref ( )
154
- . map ( |_| Counter :: counter_value_reference ( index) )
174
+ Some ( Counter :: counter_value_reference ( index) )
155
175
} else {
156
176
let index = self . expression_index ( u32:: from ( id) ) ;
157
177
self . expressions
158
178
. get ( index)
159
179
. expect ( "expression id is out of range" )
160
180
. as_ref ( )
161
- . map ( |_| Counter :: expression ( new_indexes[ index] ) )
181
+ // If an expression was optimized out, assume it would have produced a count
182
+ // of zero. This ensures that expressions dependent on optimized-out
183
+ // expressions are still valid.
184
+ . map_or ( Some ( Counter :: zero ( ) ) , |_| {
185
+ new_indexes[ index] . map ( |new_index| Counter :: expression ( new_index) )
186
+ } )
162
187
}
163
188
} ;
164
189
165
- for ( original_index, expression_region ) in
190
+ for ( original_index, expression ) in
166
191
self . expressions . iter_enumerated ( ) . filter_map ( |( original_index, entry) | {
167
192
// Option::map() will return None to filter out missing expressions. This may happen
168
193
// if, for example, a MIR-instrumented expression is removed during an optimization.
169
- entry. as_ref ( ) . map ( |region | ( original_index, region ) )
194
+ entry. as_ref ( ) . map ( |expression | ( original_index, expression ) )
170
195
} )
171
196
{
172
- let region = & expression_region . region ;
173
- let ExpressionRegion { lhs, op, rhs, .. } = * expression_region ;
197
+ let optional_region = & expression . region ;
198
+ let Expression { lhs, op, rhs, .. } = * expression ;
174
199
175
200
if let Some ( Some ( ( lhs_counter, rhs_counter) ) ) =
176
201
id_to_counter ( & new_indexes, lhs) . map ( |lhs_counter| {
177
202
id_to_counter ( & new_indexes, rhs) . map ( |rhs_counter| ( lhs_counter, rhs_counter) )
178
203
} )
179
204
{
205
+ debug_assert ! (
206
+ ( lhs_counter. id as usize )
207
+ < usize :: max( self . counters. len( ) , self . expressions. len( ) )
208
+ ) ;
209
+ debug_assert ! (
210
+ ( rhs_counter. id as usize )
211
+ < usize :: max( self . counters. len( ) , self . expressions. len( ) )
212
+ ) ;
180
213
// Both operands exist. `Expression` operands exist in `self.expressions` and have
181
214
// been assigned a `new_index`.
182
215
let mapped_expression_index =
@@ -190,12 +223,20 @@ impl FunctionCoverage {
190
223
rhs_counter,
191
224
) ;
192
225
debug ! (
193
- "Adding expression {:?} = {:?} at {:?}" ,
194
- mapped_expression_index, expression, region
226
+ "Adding expression {:?} = {:?}, region: {:?}" ,
227
+ mapped_expression_index, expression, optional_region
195
228
) ;
196
229
counter_expressions. push ( expression) ;
197
- new_indexes[ original_index] = mapped_expression_index;
198
- expression_regions. push ( ( Counter :: expression ( mapped_expression_index) , region) ) ;
230
+ new_indexes[ original_index] = Some ( mapped_expression_index) ;
231
+ if let Some ( region) = optional_region {
232
+ expression_regions. push ( ( Counter :: expression ( mapped_expression_index) , region) ) ;
233
+ }
234
+ } else {
235
+ debug ! (
236
+ "Ignoring expression with one or more missing operands: \
237
+ original_index={:?}, lhs={:?}, op={:?}, rhs={:?}, region={:?}",
238
+ original_index, lhs, op, rhs, optional_region,
239
+ )
199
240
}
200
241
}
201
242
( counter_expressions, expression_regions. into_iter ( ) )
0 commit comments