@@ -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
} ;
@@ -161,23 +165,33 @@ impl CoverageGraph {
161
165
self . dominators . as_ref ( ) . unwrap ( ) . cmp_in_dominator_order ( a, b)
162
166
}
163
167
164
- /// Returns true if the given node has 2 or more in-edges, i.e. 2 or more
165
- /// predecessors.
166
- ///
167
- /// This property is interesting to code that assigns counters to nodes and
168
- /// edges, because if a node _doesn't_ have multiple in-edges, then there's
169
- /// no benefit in having a separate counter for its in-edge, because it
170
- /// would have the same value as the node's own counter.
171
- ///
172
- /// FIXME: That assumption might not be true for [`TerminatorKind::Yield`]?
173
- #[ inline( always) ]
174
- pub ( crate ) fn bcb_has_multiple_in_edges ( & self , bcb : BasicCoverageBlock ) -> bool {
175
- // Even though bcb0 conceptually has an extra virtual in-edge due to
176
- // being the entry point, we've already asserted that it has no _other_
177
- // in-edges, so there's no possibility of it having _multiple_ in-edges.
178
- // (And since its virtual in-edge doesn't exist in the graph, that edge
179
- // can't have a separate counter anyway.)
180
- self . predecessors [ bcb] . len ( ) > 1
168
+ /// Returns the source of this node's sole in-edge, if it has exactly one.
169
+ /// That edge can be assumed to have the same execution count as the node
170
+ /// itself (in the absence of panics).
171
+ pub ( crate ) fn sole_predecessor (
172
+ & self ,
173
+ to_bcb : BasicCoverageBlock ,
174
+ ) -> Option < BasicCoverageBlock > {
175
+ // Unlike `simple_successor`, there is no need for extra checks here.
176
+ if let & [ from_bcb] = self . predecessors [ to_bcb] . as_slice ( ) { Some ( from_bcb) } else { None }
177
+ }
178
+
179
+ /// Returns the target of this node's sole out-edge, if it has exactly
180
+ /// one, but only if that edge can be assumed to have the same execution
181
+ /// count as the node itself (in the absence of panics).
182
+ pub ( crate ) fn simple_successor (
183
+ & self ,
184
+ from_bcb : BasicCoverageBlock ,
185
+ ) -> Option < BasicCoverageBlock > {
186
+ // If a node's count is the sum of its out-edges, and it has exactly
187
+ // one out-edge, then that edge has the same count as the node.
188
+ if self . bcbs [ from_bcb] . is_out_summable
189
+ && let & [ to_bcb] = self . successors [ from_bcb] . as_slice ( )
190
+ {
191
+ Some ( to_bcb)
192
+ } else {
193
+ None
194
+ }
181
195
}
182
196
}
183
197
@@ -266,14 +280,16 @@ rustc_index::newtype_index! {
266
280
#[ derive( Debug , Clone ) ]
267
281
pub ( crate ) struct BasicCoverageBlockData {
268
282
pub ( crate ) basic_blocks : Vec < BasicBlock > ,
283
+
284
+ /// If true, this node's execution count can be assumed to be the sum of the
285
+ /// execution counts of all of its **out-edges** (assuming no panics).
286
+ ///
287
+ /// Notably, this is false for a node ending with [`TerminatorKind::Yield`],
288
+ /// because the yielding coroutine might not be resumed.
289
+ pub ( crate ) is_out_summable : bool ,
269
290
}
270
291
271
292
impl BasicCoverageBlockData {
272
- fn from ( basic_blocks : Vec < BasicBlock > ) -> Self {
273
- assert ! ( basic_blocks. len( ) > 0 ) ;
274
- Self { basic_blocks }
275
- }
276
-
277
293
#[ inline( always) ]
278
294
pub ( crate ) fn leader_bb ( & self ) -> BasicBlock {
279
295
self . basic_blocks [ 0 ]
@@ -295,13 +311,27 @@ enum CoverageSuccessors<'a> {
295
311
Chainable ( BasicBlock ) ,
296
312
/// The block cannot be combined into the same BCB as its successor(s).
297
313
NotChainable ( & ' a [ BasicBlock ] ) ,
314
+ /// Yield terminators are not chainable, and their execution count can also
315
+ /// differ from the execution count of their out-edge.
316
+ Yield ( BasicBlock ) ,
298
317
}
299
318
300
319
impl CoverageSuccessors < ' _ > {
301
320
fn is_chainable ( & self ) -> bool {
302
321
match self {
303
322
Self :: Chainable ( _) => true ,
304
323
Self :: NotChainable ( _) => false ,
324
+ Self :: Yield ( _) => false ,
325
+ }
326
+ }
327
+
328
+ /// Returns true if the terminator itself is assumed to have the same
329
+ /// execution count as the sum of its out-edges (assuming no panics).
330
+ fn is_out_summable ( & self ) -> bool {
331
+ match self {
332
+ Self :: Chainable ( _) => true ,
333
+ Self :: NotChainable ( _) => true ,
334
+ Self :: Yield ( _) => false ,
305
335
}
306
336
}
307
337
}
@@ -312,7 +342,9 @@ impl IntoIterator for CoverageSuccessors<'_> {
312
342
313
343
fn into_iter ( self ) -> Self :: IntoIter {
314
344
match self {
315
- Self :: Chainable ( bb) => Some ( bb) . into_iter ( ) . chain ( ( & [ ] ) . iter ( ) . copied ( ) ) ,
345
+ Self :: Chainable ( bb) | Self :: Yield ( bb) => {
346
+ Some ( bb) . into_iter ( ) . chain ( ( & [ ] ) . iter ( ) . copied ( ) )
347
+ }
316
348
Self :: NotChainable ( bbs) => None . into_iter ( ) . chain ( bbs. iter ( ) . copied ( ) ) ,
317
349
}
318
350
}
@@ -331,7 +363,7 @@ fn bcb_filtered_successors<'a, 'tcx>(terminator: &'a Terminator<'tcx>) -> Covera
331
363
332
364
// A yield terminator has exactly 1 successor, but should not be chained,
333
365
// because its resume edge has a different execution count.
334
- Yield { ref resume, .. } => CoverageSuccessors :: NotChainable ( std :: slice :: from_ref ( resume) ) ,
366
+ Yield { resume, .. } => CoverageSuccessors :: Yield ( resume) ,
335
367
336
368
// These terminators have exactly one coverage-relevant successor,
337
369
// and can be chained into it.
@@ -341,15 +373,15 @@ fn bcb_filtered_successors<'a, 'tcx>(terminator: &'a Terminator<'tcx>) -> Covera
341
373
| FalseUnwind { real_target : target, .. }
342
374
| Goto { target } => CoverageSuccessors :: Chainable ( target) ,
343
375
344
- // A call terminator can normally be chained, except when they have no
345
- // successor because they are known to diverge.
376
+ // A call terminator can normally be chained, except when it has no
377
+ // successor because it is known to diverge.
346
378
Call { target : maybe_target, .. } => match maybe_target {
347
379
Some ( target) => CoverageSuccessors :: Chainable ( target) ,
348
380
None => CoverageSuccessors :: NotChainable ( & [ ] ) ,
349
381
} ,
350
382
351
- // An inline asm terminator can normally be chained, except when it diverges or uses asm
352
- // goto.
383
+ // An inline asm terminator can normally be chained, except when it
384
+ // diverges or uses asm goto.
353
385
InlineAsm { ref targets, .. } => {
354
386
if let [ target] = targets[ ..] {
355
387
CoverageSuccessors :: Chainable ( target)
0 commit comments