@@ -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