@@ -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 } ;
@@ -161,23 +165,33 @@ impl CoverageGraph {
161165 self . dominators . as_ref ( ) . unwrap ( ) . cmp_in_dominator_order ( a, b)
162166 }
163167
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+ }
181195 }
182196}
183197
@@ -266,14 +280,16 @@ rustc_index::newtype_index! {
266280#[ derive( Debug , Clone ) ]
267281pub ( crate ) struct BasicCoverageBlockData {
268282 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 ,
269290}
270291
271292impl BasicCoverageBlockData {
272- fn from ( basic_blocks : Vec < BasicBlock > ) -> Self {
273- assert ! ( basic_blocks. len( ) > 0 ) ;
274- Self { basic_blocks }
275- }
276-
277293 #[ inline( always) ]
278294 pub ( crate ) fn leader_bb ( & self ) -> BasicBlock {
279295 self . basic_blocks [ 0 ]
@@ -295,13 +311,27 @@ enum CoverageSuccessors<'a> {
295311 Chainable ( BasicBlock ) ,
296312 /// The block cannot be combined into the same BCB as its successor(s).
297313 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 ) ,
298317}
299318
300319impl CoverageSuccessors < ' _ > {
301320 fn is_chainable ( & self ) -> bool {
302321 match self {
303322 Self :: Chainable ( _) => true ,
304323 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 ,
305335 }
306336 }
307337}
@@ -312,7 +342,9 @@ impl IntoIterator for CoverageSuccessors<'_> {
312342
313343 fn into_iter ( self ) -> Self :: IntoIter {
314344 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+ }
316348 Self :: NotChainable ( bbs) => None . into_iter ( ) . chain ( bbs. iter ( ) . copied ( ) ) ,
317349 }
318350 }
@@ -331,7 +363,7 @@ fn bcb_filtered_successors<'a, 'tcx>(terminator: &'a Terminator<'tcx>) -> Covera
331363
332364 // A yield terminator has exactly 1 successor, but should not be chained,
333365 // 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) ,
335367
336368 // These terminators have exactly one coverage-relevant successor,
337369 // and can be chained into it.
@@ -341,15 +373,15 @@ fn bcb_filtered_successors<'a, 'tcx>(terminator: &'a Terminator<'tcx>) -> Covera
341373 | FalseUnwind { real_target : target, .. }
342374 | Goto { target } => CoverageSuccessors :: Chainable ( target) ,
343375
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.
346378 Call { target : maybe_target, .. } => match maybe_target {
347379 Some ( target) => CoverageSuccessors :: Chainable ( target) ,
348380 None => CoverageSuccessors :: NotChainable ( & [ ] ) ,
349381 } ,
350382
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.
353385 InlineAsm { ref targets, .. } => {
354386 if let [ target] = targets[ ..] {
355387 CoverageSuccessors :: Chainable ( target)
0 commit comments