1
1
use std:: cmp:: Ordering ;
2
2
use std:: collections:: VecDeque ;
3
+ use std:: iter;
3
4
use std:: ops:: { Index , IndexMut } ;
4
5
5
6
use rustc_data_structures:: captures:: Captures ;
6
7
use rustc_data_structures:: fx:: FxHashSet ;
7
- use rustc_data_structures:: graph:: dominators:: { self , Dominators } ;
8
+ use rustc_data_structures:: graph:: dominators:: Dominators ;
8
9
use rustc_data_structures:: graph:: { self , DirectedGraph , StartNode } ;
9
10
use rustc_index:: IndexVec ;
10
11
use rustc_index:: bit_set:: BitSet ;
@@ -20,11 +21,17 @@ pub(crate) struct CoverageGraph {
20
21
bb_to_bcb : IndexVec < BasicBlock , Option < BasicCoverageBlock > > ,
21
22
pub ( crate ) successors : IndexVec < BasicCoverageBlock , Vec < BasicCoverageBlock > > ,
22
23
pub ( crate ) predecessors : IndexVec < BasicCoverageBlock , Vec < BasicCoverageBlock > > ,
24
+
23
25
dominators : Option < Dominators < BasicCoverageBlock > > ,
24
26
/// Allows nodes to be compared in some total order such that _if_
25
27
/// `a` dominates `b`, then `a < b`. If neither node dominates the other,
26
28
/// their relative order is consistent but arbitrary.
27
29
dominator_order_rank : IndexVec < BasicCoverageBlock , u32 > ,
30
+ /// A loop header is a node that dominates one or more of its predecessors.
31
+ is_loop_header : BitSet < BasicCoverageBlock > ,
32
+ /// For each node, the loop header node of its nearest enclosing loop.
33
+ /// This forms a linked list that can be traversed to find all enclosing loops.
34
+ enclosing_loop_header : IndexVec < BasicCoverageBlock , Option < BasicCoverageBlock > > ,
28
35
}
29
36
30
37
impl CoverageGraph {
@@ -66,17 +73,38 @@ impl CoverageGraph {
66
73
predecessors,
67
74
dominators : None ,
68
75
dominator_order_rank : IndexVec :: from_elem_n ( 0 , num_nodes) ,
76
+ is_loop_header : BitSet :: new_empty ( num_nodes) ,
77
+ enclosing_loop_header : IndexVec :: from_elem_n ( None , num_nodes) ,
69
78
} ;
70
79
assert_eq ! ( num_nodes, this. num_nodes( ) ) ;
71
80
72
- this. dominators = Some ( dominators:: dominators ( & this) ) ;
81
+ // Set the dominators first, because later init steps rely on them.
82
+ this. dominators = Some ( graph:: dominators:: dominators ( & this) ) ;
73
83
74
- // The dominator rank of each node is just its index in a reverse-postorder traversal.
75
- let reverse_post_order = graph:: iterate:: reverse_post_order ( & this, this. start_node ( ) ) ;
84
+ // Iterate over all nodes, such that dominating nodes are visited before
85
+ // the nodes they dominate. Either preorder or reverse postorder is fine.
86
+ let dominator_order = graph:: iterate:: reverse_post_order ( & this, this. start_node ( ) ) ;
76
87
// The coverage graph is created by traversal, so all nodes are reachable.
77
- assert_eq ! ( reverse_post_order. len( ) , this. num_nodes( ) ) ;
78
- for ( rank, bcb) in ( 0u32 ..) . zip ( reverse_post_order) {
88
+ assert_eq ! ( dominator_order. len( ) , this. num_nodes( ) ) ;
89
+ for ( rank, bcb) in ( 0u32 ..) . zip ( dominator_order) {
90
+ // The dominator rank of each node is its index in a dominator-order traversal.
79
91
this. dominator_order_rank [ bcb] = rank;
92
+
93
+ // A node is a loop header if it dominates any of its predecessors.
94
+ if this. reloop_predecessors ( bcb) . next ( ) . is_some ( ) {
95
+ this. is_loop_header . insert ( bcb) ;
96
+ }
97
+
98
+ // If the immediate dominator is a loop header, that's our enclosing loop.
99
+ // Otherwise, inherit the immediate dominator's enclosing loop.
100
+ // (Dominator order ensures that we already processed the dominator.)
101
+ if let Some ( dom) = this. dominators ( ) . immediate_dominator ( bcb) {
102
+ this. enclosing_loop_header [ bcb] = this
103
+ . is_loop_header
104
+ . contains ( dom)
105
+ . then_some ( dom)
106
+ . or_else ( || this. enclosing_loop_header [ dom] ) ;
107
+ }
80
108
}
81
109
82
110
// The coverage graph's entry-point node (bcb0) always starts with bb0,
@@ -172,9 +200,14 @@ impl CoverageGraph {
172
200
if bb. index ( ) < self . bb_to_bcb . len ( ) { self . bb_to_bcb [ bb] } else { None }
173
201
}
174
202
203
+ #[ inline( always) ]
204
+ fn dominators ( & self ) -> & Dominators < BasicCoverageBlock > {
205
+ self . dominators . as_ref ( ) . unwrap ( )
206
+ }
207
+
175
208
#[ inline( always) ]
176
209
pub ( crate ) fn dominates ( & self , dom : BasicCoverageBlock , node : BasicCoverageBlock ) -> bool {
177
- self . dominators . as_ref ( ) . unwrap ( ) . dominates ( dom, node)
210
+ self . dominators ( ) . dominates ( dom, node)
178
211
}
179
212
180
213
#[ inline( always) ]
@@ -214,6 +247,36 @@ impl CoverageGraph {
214
247
None
215
248
}
216
249
}
250
+
251
+ /// For each loop that contains the given node, yields the "loop header"
252
+ /// node representing that loop, from innermost to outermost. If the given
253
+ /// node is itself a loop header, it is yielded first.
254
+ pub ( crate ) fn loop_headers_containing (
255
+ & self ,
256
+ bcb : BasicCoverageBlock ,
257
+ ) -> impl Iterator < Item = BasicCoverageBlock > + Captures < ' _ > {
258
+ let self_if_loop_header = self . is_loop_header . contains ( bcb) . then_some ( bcb) . into_iter ( ) ;
259
+
260
+ let mut curr = Some ( bcb) ;
261
+ let strictly_enclosing = iter:: from_fn ( move || {
262
+ let enclosing = self . enclosing_loop_header [ curr?] ;
263
+ curr = enclosing;
264
+ enclosing
265
+ } ) ;
266
+
267
+ self_if_loop_header. chain ( strictly_enclosing)
268
+ }
269
+
270
+ /// For the given node, yields the subset of its predecessor nodes that
271
+ /// it dominates. If that subset is non-empty, the node is a "loop header",
272
+ /// and each of those predecessors represents an in-edge that jumps back to
273
+ /// the top of its loop.
274
+ pub ( crate ) fn reloop_predecessors (
275
+ & self ,
276
+ to_bcb : BasicCoverageBlock ,
277
+ ) -> impl Iterator < Item = BasicCoverageBlock > + Captures < ' _ > {
278
+ self . predecessors [ to_bcb] . iter ( ) . copied ( ) . filter ( move |& pred| self . dominates ( to_bcb, pred) )
279
+ }
217
280
}
218
281
219
282
impl Index < BasicCoverageBlock > for CoverageGraph {
@@ -439,15 +502,12 @@ struct TraversalContext {
439
502
pub ( crate ) struct TraverseCoverageGraphWithLoops < ' a > {
440
503
basic_coverage_blocks : & ' a CoverageGraph ,
441
504
442
- backedges : IndexVec < BasicCoverageBlock , Vec < BasicCoverageBlock > > ,
443
505
context_stack : Vec < TraversalContext > ,
444
506
visited : BitSet < BasicCoverageBlock > ,
445
507
}
446
508
447
509
impl < ' a > TraverseCoverageGraphWithLoops < ' a > {
448
510
pub ( crate ) fn new ( basic_coverage_blocks : & ' a CoverageGraph ) -> Self {
449
- let backedges = find_loop_backedges ( basic_coverage_blocks) ;
450
-
451
511
let worklist = VecDeque :: from ( [ basic_coverage_blocks. start_node ( ) ] ) ;
452
512
let context_stack = vec ! [ TraversalContext { loop_header: None , worklist } ] ;
453
513
@@ -456,17 +516,7 @@ impl<'a> TraverseCoverageGraphWithLoops<'a> {
456
516
// of the stack as loops are entered, and popped off of the stack when a loop's worklist is
457
517
// exhausted.
458
518
let visited = BitSet :: new_empty ( basic_coverage_blocks. num_nodes ( ) ) ;
459
- Self { basic_coverage_blocks, backedges, context_stack, visited }
460
- }
461
-
462
- /// For each loop on the loop context stack (top-down), yields a list of BCBs
463
- /// within that loop that have an outgoing edge back to the loop header.
464
- pub ( crate ) fn reloop_bcbs_per_loop ( & self ) -> impl Iterator < Item = & [ BasicCoverageBlock ] > {
465
- self . context_stack
466
- . iter ( )
467
- . rev ( )
468
- . filter_map ( |context| context. loop_header )
469
- . map ( |header_bcb| self . backedges [ header_bcb] . as_slice ( ) )
519
+ Self { basic_coverage_blocks, context_stack, visited }
470
520
}
471
521
472
522
pub ( crate ) fn next ( & mut self ) -> Option < BasicCoverageBlock > {
@@ -488,7 +538,7 @@ impl<'a> TraverseCoverageGraphWithLoops<'a> {
488
538
}
489
539
debug ! ( "Visiting {bcb:?}" ) ;
490
540
491
- if self . backedges [ bcb ] . len ( ) > 0 {
541
+ if self . basic_coverage_blocks . is_loop_header . contains ( bcb ) {
492
542
debug ! ( "{bcb:?} is a loop header! Start a new TraversalContext..." ) ;
493
543
self . context_stack
494
544
. push ( TraversalContext { loop_header : Some ( bcb) , worklist : VecDeque :: new ( ) } ) ;
@@ -566,29 +616,6 @@ impl<'a> TraverseCoverageGraphWithLoops<'a> {
566
616
}
567
617
}
568
618
569
- fn find_loop_backedges (
570
- basic_coverage_blocks : & CoverageGraph ,
571
- ) -> IndexVec < BasicCoverageBlock , Vec < BasicCoverageBlock > > {
572
- let num_bcbs = basic_coverage_blocks. num_nodes ( ) ;
573
- let mut backedges = IndexVec :: from_elem_n ( Vec :: < BasicCoverageBlock > :: new ( ) , num_bcbs) ;
574
-
575
- // Identify loops by their backedges.
576
- for ( bcb, _) in basic_coverage_blocks. iter_enumerated ( ) {
577
- for & successor in & basic_coverage_blocks. successors [ bcb] {
578
- if basic_coverage_blocks. dominates ( successor, bcb) {
579
- let loop_header = successor;
580
- let backedge_from_bcb = bcb;
581
- debug ! (
582
- "Found BCB backedge: {:?} -> loop_header: {:?}" ,
583
- backedge_from_bcb, loop_header
584
- ) ;
585
- backedges[ loop_header] . push ( backedge_from_bcb) ;
586
- }
587
- }
588
- }
589
- backedges
590
- }
591
-
592
619
fn short_circuit_preorder < ' a , ' tcx , F , Iter > (
593
620
body : & ' a mir:: Body < ' tcx > ,
594
621
filtered_successors : F ,
0 commit comments