@@ -87,7 +87,11 @@ impl CoverageGraph {
87
87
for & bb in basic_blocks. iter ( ) {
88
88
bb_to_bcb[ bb] = Some ( bcb) ;
89
89
}
90
- let bcb_data = BasicCoverageBlockData :: from ( basic_blocks) ;
90
+
91
+ let is_out_summable = basic_blocks. last ( ) . map_or ( false , |& bb| {
92
+ bcb_filtered_successors ( mir_body[ bb] . terminator ( ) ) . is_out_summable ( )
93
+ } ) ;
94
+ let bcb_data = BasicCoverageBlockData { basic_blocks, is_out_summable } ;
91
95
debug ! ( "adding bcb{}: {:?}" , bcb. index( ) , bcb_data) ;
92
96
bcbs. push ( bcb_data) ;
93
97
} ;
@@ -168,8 +172,6 @@ impl CoverageGraph {
168
172
/// edges, because if a node _doesn't_ have multiple in-edges, then there's
169
173
/// no benefit in having a separate counter for its in-edge, because it
170
174
/// would have the same value as the node's own counter.
171
- ///
172
- /// FIXME: That assumption might not be true for [`TerminatorKind::Yield`]?
173
175
#[ inline( always) ]
174
176
pub ( crate ) fn bcb_has_multiple_in_edges ( & self , bcb : BasicCoverageBlock ) -> bool {
175
177
// Even though bcb0 conceptually has an extra virtual in-edge due to
@@ -179,6 +181,24 @@ impl CoverageGraph {
179
181
// can't have a separate counter anyway.)
180
182
self . predecessors [ bcb] . len ( ) > 1
181
183
}
184
+
185
+ /// Returns the target of this node's sole out-edge, if it has exactly
186
+ /// one, but only if that edge can be assumed to have the same execution
187
+ /// count as the node itself (in the absence of panics).
188
+ pub ( crate ) fn simple_successor (
189
+ & self ,
190
+ from_bcb : BasicCoverageBlock ,
191
+ ) -> Option < BasicCoverageBlock > {
192
+ // If a node's count is the sum of its out-edges, and it has exactly
193
+ // one out-edge, then that edge has the same count as the node.
194
+ if self . bcbs [ from_bcb] . is_out_summable
195
+ && let & [ to_bcb] = self . successors [ from_bcb] . as_slice ( )
196
+ {
197
+ Some ( to_bcb)
198
+ } else {
199
+ None
200
+ }
201
+ }
182
202
}
183
203
184
204
impl Index < BasicCoverageBlock > for CoverageGraph {
@@ -266,14 +286,16 @@ rustc_index::newtype_index! {
266
286
#[ derive( Debug , Clone ) ]
267
287
pub ( crate ) struct BasicCoverageBlockData {
268
288
pub ( crate ) basic_blocks : Vec < BasicBlock > ,
289
+
290
+ /// If true, this node's execution count can be assumed to be the sum of the
291
+ /// execution counts of all of its **out-edges** (assuming no panics).
292
+ ///
293
+ /// Notably, this is false for a node ending with [`TerminatorKind::Yield`],
294
+ /// because the yielding coroutine might not be resumed.
295
+ pub ( crate ) is_out_summable : bool ,
269
296
}
270
297
271
298
impl BasicCoverageBlockData {
272
- fn from ( basic_blocks : Vec < BasicBlock > ) -> Self {
273
- assert ! ( basic_blocks. len( ) > 0 ) ;
274
- Self { basic_blocks }
275
- }
276
-
277
299
#[ inline( always) ]
278
300
pub ( crate ) fn leader_bb ( & self ) -> BasicBlock {
279
301
self . basic_blocks [ 0 ]
@@ -295,13 +317,27 @@ enum CoverageSuccessors<'a> {
295
317
Chainable ( BasicBlock ) ,
296
318
/// The block cannot be combined into the same BCB as its successor(s).
297
319
NotChainable ( & ' a [ BasicBlock ] ) ,
320
+ /// Yield terminators are not chainable, and their execution count can also
321
+ /// differ from the execution count of their out-edge.
322
+ Yield ( BasicBlock ) ,
298
323
}
299
324
300
325
impl CoverageSuccessors < ' _ > {
301
326
fn is_chainable ( & self ) -> bool {
302
327
match self {
303
328
Self :: Chainable ( _) => true ,
304
329
Self :: NotChainable ( _) => false ,
330
+ Self :: Yield ( _) => false ,
331
+ }
332
+ }
333
+
334
+ /// Returns true if the terminator itself is assumed to have the same
335
+ /// execution count as the sum of its out-edges (assuming no panics).
336
+ fn is_out_summable ( & self ) -> bool {
337
+ match self {
338
+ Self :: Chainable ( _) => true ,
339
+ Self :: NotChainable ( _) => true ,
340
+ Self :: Yield ( _) => false ,
305
341
}
306
342
}
307
343
}
@@ -312,7 +348,9 @@ impl IntoIterator for CoverageSuccessors<'_> {
312
348
313
349
fn into_iter ( self ) -> Self :: IntoIter {
314
350
match self {
315
- Self :: Chainable ( bb) => Some ( bb) . into_iter ( ) . chain ( ( & [ ] ) . iter ( ) . copied ( ) ) ,
351
+ Self :: Chainable ( bb) | Self :: Yield ( bb) => {
352
+ Some ( bb) . into_iter ( ) . chain ( ( & [ ] ) . iter ( ) . copied ( ) )
353
+ }
316
354
Self :: NotChainable ( bbs) => None . into_iter ( ) . chain ( bbs. iter ( ) . copied ( ) ) ,
317
355
}
318
356
}
@@ -331,7 +369,7 @@ fn bcb_filtered_successors<'a, 'tcx>(terminator: &'a Terminator<'tcx>) -> Covera
331
369
332
370
// A yield terminator has exactly 1 successor, but should not be chained,
333
371
// because its resume edge has a different execution count.
334
- Yield { ref resume, .. } => CoverageSuccessors :: NotChainable ( std :: slice :: from_ref ( resume) ) ,
372
+ Yield { resume, .. } => CoverageSuccessors :: Yield ( resume) ,
335
373
336
374
// These terminators have exactly one coverage-relevant successor,
337
375
// and can be chained into it.
0 commit comments