@@ -87,7 +87,11 @@ impl CoverageGraph {
8787 for & bb in basic_blocks. iter ( ) {
8888 bb_to_bcb[ bb] = Some ( bcb) ;
8989 }
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 } ;
9195 debug ! ( "adding bcb{}: {:?}" , bcb. index( ) , bcb_data) ;
9296 bcbs. push ( bcb_data) ;
9397 } ;
@@ -168,8 +172,6 @@ impl CoverageGraph {
168172 /// edges, because if a node _doesn't_ have multiple in-edges, then there's
169173 /// no benefit in having a separate counter for its in-edge, because it
170174 /// would have the same value as the node's own counter.
171- ///
172- /// FIXME: That assumption might not be true for [`TerminatorKind::Yield`]?
173175 #[ inline( always) ]
174176 pub ( crate ) fn bcb_has_multiple_in_edges ( & self , bcb : BasicCoverageBlock ) -> bool {
175177 // Even though bcb0 conceptually has an extra virtual in-edge due to
@@ -179,6 +181,24 @@ impl CoverageGraph {
179181 // can't have a separate counter anyway.)
180182 self . predecessors [ bcb] . len ( ) > 1
181183 }
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+ }
182202}
183203
184204impl Index < BasicCoverageBlock > for CoverageGraph {
@@ -266,14 +286,16 @@ rustc_index::newtype_index! {
266286#[ derive( Debug , Clone ) ]
267287pub ( crate ) struct BasicCoverageBlockData {
268288 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 ,
269296}
270297
271298impl BasicCoverageBlockData {
272- fn from ( basic_blocks : Vec < BasicBlock > ) -> Self {
273- assert ! ( basic_blocks. len( ) > 0 ) ;
274- Self { basic_blocks }
275- }
276-
277299 #[ inline( always) ]
278300 pub ( crate ) fn leader_bb ( & self ) -> BasicBlock {
279301 self . basic_blocks [ 0 ]
@@ -295,13 +317,27 @@ enum CoverageSuccessors<'a> {
295317 Chainable ( BasicBlock ) ,
296318 /// The block cannot be combined into the same BCB as its successor(s).
297319 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 ) ,
298323}
299324
300325impl CoverageSuccessors < ' _ > {
301326 fn is_chainable ( & self ) -> bool {
302327 match self {
303328 Self :: Chainable ( _) => true ,
304329 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 ,
305341 }
306342 }
307343}
@@ -312,7 +348,9 @@ impl IntoIterator for CoverageSuccessors<'_> {
312348
313349 fn into_iter ( self ) -> Self :: IntoIter {
314350 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+ }
316354 Self :: NotChainable ( bbs) => None . into_iter ( ) . chain ( bbs. iter ( ) . copied ( ) ) ,
317355 }
318356 }
@@ -331,7 +369,7 @@ fn bcb_filtered_successors<'a, 'tcx>(terminator: &'a Terminator<'tcx>) -> Covera
331369
332370 // A yield terminator has exactly 1 successor, but should not be chained,
333371 // 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) ,
335373
336374 // These terminators have exactly one coverage-relevant successor,
337375 // and can be chained into it.
0 commit comments