@@ -134,8 +134,6 @@ impl<'tcx> FunctionCoverage<'tcx> {
134
134
}
135
135
136
136
pub ( crate ) fn finalize ( & mut self ) {
137
- self . simplify_expressions ( ) ;
138
-
139
137
// Reorder the collected mappings so that counter mappings are first and
140
138
// zero mappings are last, matching the historical order.
141
139
self . mappings . sort_by_key ( |mapping| match mapping. term {
@@ -145,32 +143,37 @@ impl<'tcx> FunctionCoverage<'tcx> {
145
143
} ) ;
146
144
}
147
145
148
- /// Perform some simplifications to make the final coverage mappings
149
- /// slightly smaller.
146
+ /// Identify expressions that will always have a value of zero, and note
147
+ /// their IDs in `zero_expressions`. Mappings that refer to a zero expression
148
+ /// can instead become mappings to a constant zero value.
150
149
///
151
150
/// This method mainly exists to preserve the simplifications that were
152
151
/// already being performed by the Rust-side expression renumbering, so that
153
152
/// the resulting coverage mappings don't get worse.
154
- fn simplify_expressions ( & mut self ) {
153
+ fn identify_zero_expressions ( & self ) -> ZeroExpressions {
155
154
// The set of expressions that either were optimized out entirely, or
156
155
// have zero as both of their operands, and will therefore always have
157
156
// a value of zero. Other expressions that refer to these as operands
158
157
// can have those operands replaced with `CovTerm::Zero`.
159
158
let mut zero_expressions = FxIndexSet :: default ( ) ;
160
159
161
- // For each expression, perform simplifications based on lower-numbered
162
- // expressions, and then update the set of always-zero expressions if
163
- // necessary.
160
+ // Simplify a copy of each expression based on lower-numbered expressions,
161
+ // and then update the set of always-zero expressions if necessary.
164
162
// (By construction, expressions can only refer to other expressions
165
- // that have lower IDs, so one simplification pass is sufficient.)
166
- for ( id, maybe_expression) in self . expressions . iter_enumerated_mut ( ) {
163
+ // that have lower IDs, so one pass is sufficient.)
164
+ for ( id, maybe_expression) in self . expressions . iter_enumerated ( ) {
167
165
let Some ( expression) = maybe_expression else {
168
166
// If an expression is missing, it must have been optimized away,
169
167
// so any operand that refers to it can be replaced with zero.
170
168
zero_expressions. insert ( id) ;
171
169
continue ;
172
170
} ;
173
171
172
+ // We don't need to simplify the actual expression data in the
173
+ // expressions list; we can just simplify a temporary copy and then
174
+ // use that to update the set of always-zero expressions.
175
+ let mut expression = expression. clone ( ) ;
176
+
174
177
// If an operand refers to an expression that is always zero, then
175
178
// that operand can be replaced with `CovTerm::Zero`.
176
179
let maybe_set_operand_to_zero = |operand : & mut CovTerm | match & * operand {
@@ -195,6 +198,8 @@ impl<'tcx> FunctionCoverage<'tcx> {
195
198
zero_expressions. insert ( id) ;
196
199
}
197
200
}
201
+
202
+ ZeroExpressions ( zero_expressions)
198
203
}
199
204
200
205
/// Return the source hash, generated from the HIR node structure, and used to indicate whether
@@ -209,7 +214,9 @@ impl<'tcx> FunctionCoverage<'tcx> {
209
214
pub fn get_expressions_and_counter_regions (
210
215
& self ,
211
216
) -> ( Vec < CounterExpression > , impl Iterator < Item = ( Counter , & CodeRegion ) > ) {
212
- let counter_expressions = self . counter_expressions ( ) ;
217
+ let zero_expressions = self . identify_zero_expressions ( ) ;
218
+
219
+ let counter_expressions = self . counter_expressions ( & zero_expressions) ;
213
220
// Expression IDs are indices into `self.expressions`, and on the LLVM
214
221
// side they will be treated as indices into `counter_expressions`, so
215
222
// the two vectors should correspond 1:1.
@@ -222,12 +229,17 @@ impl<'tcx> FunctionCoverage<'tcx> {
222
229
223
230
/// Convert this function's coverage expression data into a form that can be
224
231
/// passed through FFI to LLVM.
225
- fn counter_expressions ( & self ) -> Vec < CounterExpression > {
232
+ fn counter_expressions ( & self , zero_expressions : & ZeroExpressions ) -> Vec < CounterExpression > {
226
233
// We know that LLVM will optimize out any unused expressions before
227
234
// producing the final coverage map, so there's no need to do the same
228
235
// thing on the Rust side unless we're confident we can do much better.
229
236
// (See `CounterExpressionsMinimizer` in `CoverageMappingWriter.cpp`.)
230
237
238
+ let counter_from_operand = |operand : CovTerm | match operand {
239
+ CovTerm :: Expression ( id) if zero_expressions. contains ( id) => Counter :: ZERO ,
240
+ _ => Counter :: from_term ( operand) ,
241
+ } ;
242
+
231
243
self . expressions
232
244
. iter ( )
233
245
. map ( |expression| match expression {
@@ -241,12 +253,12 @@ impl<'tcx> FunctionCoverage<'tcx> {
241
253
& Some ( Expression { lhs, op, rhs, .. } ) => {
242
254
// Convert the operands and operator as normal.
243
255
CounterExpression :: new (
244
- Counter :: from_term ( lhs) ,
256
+ counter_from_operand ( lhs) ,
245
257
match op {
246
258
Op :: Add => ExprKind :: Add ,
247
259
Op :: Subtract => ExprKind :: Subtract ,
248
260
} ,
249
- Counter :: from_term ( rhs) ,
261
+ counter_from_operand ( rhs) ,
250
262
)
251
263
}
252
264
} )
@@ -262,3 +274,14 @@ impl<'tcx> FunctionCoverage<'tcx> {
262
274
} )
263
275
}
264
276
}
277
+
278
+ /// Set of expression IDs that are known to always evaluate to zero.
279
+ /// Any mapping or expression operand that refers to these expressions can have
280
+ /// that reference replaced with a constant zero value.
281
+ struct ZeroExpressions ( FxIndexSet < ExpressionId > ) ;
282
+
283
+ impl ZeroExpressions {
284
+ fn contains ( & self , id : ExpressionId ) -> bool {
285
+ self . 0 . contains ( & id)
286
+ }
287
+ }
0 commit comments