1
- use crate :: mir:: graph_cyclic_cache:: GraphIsCyclicCache ;
2
- use crate :: mir:: predecessors:: { PredecessorCache , Predecessors } ;
3
- use crate :: mir:: switch_sources:: { SwitchSourceCache , SwitchSources } ;
4
- use crate :: mir:: traversal:: PostorderCache ;
5
- use crate :: mir:: { BasicBlock , BasicBlockData , Successors , START_BLOCK } ;
1
+ use crate :: mir:: traversal:: Postorder ;
2
+ use crate :: mir:: { BasicBlock , BasicBlockData , Successors , Terminator , TerminatorKind , START_BLOCK } ;
6
3
4
+ use rustc_data_structures:: fx:: FxHashMap ;
7
5
use rustc_data_structures:: graph;
8
6
use rustc_data_structures:: graph:: dominators:: { dominators, Dominators } ;
7
+ use rustc_data_structures:: stable_hasher:: { HashStable , StableHasher } ;
8
+ use rustc_data_structures:: sync:: OnceCell ;
9
9
use rustc_index:: vec:: IndexVec ;
10
+ use rustc_serialize:: { Decodable , Decoder , Encodable , Encoder } ;
11
+ use smallvec:: SmallVec ;
10
12
11
13
#[ derive( Clone , TyEncodable , TyDecodable , Debug , HashStable , TypeFoldable , TypeVisitable ) ]
12
14
pub struct BasicBlocks < ' tcx > {
13
15
basic_blocks : IndexVec < BasicBlock , BasicBlockData < ' tcx > > ,
14
- predecessor_cache : PredecessorCache ,
15
- switch_source_cache : SwitchSourceCache ,
16
- is_cyclic : GraphIsCyclicCache ,
17
- postorder_cache : PostorderCache ,
16
+ cache : Cache ,
17
+ }
18
+
19
+ // Typically 95%+ of basic blocks have 4 or fewer predecessors.
20
+ pub type Predecessors = IndexVec < BasicBlock , SmallVec < [ BasicBlock ; 4 ] > > ;
21
+
22
+ pub type SwitchSources = FxHashMap < ( BasicBlock , BasicBlock ) , SmallVec < [ Option < u128 > ; 1 ] > > ;
23
+
24
+ #[ derive( Clone , Default , Debug ) ]
25
+ struct Cache {
26
+ predecessors : OnceCell < Predecessors > ,
27
+ switch_sources : OnceCell < SwitchSources > ,
28
+ is_cyclic : OnceCell < bool > ,
29
+ postorder : OnceCell < Vec < BasicBlock > > ,
18
30
}
19
31
20
32
impl < ' tcx > BasicBlocks < ' tcx > {
21
33
#[ inline]
22
34
pub fn new ( basic_blocks : IndexVec < BasicBlock , BasicBlockData < ' tcx > > ) -> Self {
23
- BasicBlocks {
24
- basic_blocks,
25
- predecessor_cache : PredecessorCache :: new ( ) ,
26
- switch_source_cache : SwitchSourceCache :: new ( ) ,
27
- is_cyclic : GraphIsCyclicCache :: new ( ) ,
28
- postorder_cache : PostorderCache :: new ( ) ,
29
- }
35
+ BasicBlocks { basic_blocks, cache : Cache :: default ( ) }
30
36
}
31
37
32
38
/// Returns true if control-flow graph contains a cycle reachable from the `START_BLOCK`.
33
39
#[ inline]
34
40
pub fn is_cfg_cyclic ( & self ) -> bool {
35
- self . is_cyclic . is_cyclic ( self )
41
+ * self . cache . is_cyclic . get_or_init ( || graph :: is_cyclic ( self ) )
36
42
}
37
43
38
44
#[ inline]
@@ -43,20 +49,46 @@ impl<'tcx> BasicBlocks<'tcx> {
43
49
/// Returns predecessors for each basic block.
44
50
#[ inline]
45
51
pub fn predecessors ( & self ) -> & Predecessors {
46
- self . predecessor_cache . compute ( & self . basic_blocks )
52
+ self . cache . predecessors . get_or_init ( || {
53
+ let mut preds = IndexVec :: from_elem ( SmallVec :: new ( ) , & self . basic_blocks ) ;
54
+ for ( bb, data) in self . basic_blocks . iter_enumerated ( ) {
55
+ if let Some ( term) = & data. terminator {
56
+ for succ in term. successors ( ) {
57
+ preds[ succ] . push ( bb) ;
58
+ }
59
+ }
60
+ }
61
+ preds
62
+ } )
47
63
}
48
64
49
65
/// Returns basic blocks in a postorder.
50
66
#[ inline]
51
67
pub fn postorder ( & self ) -> & [ BasicBlock ] {
52
- self . postorder_cache . compute ( & self . basic_blocks )
68
+ self . cache . postorder . get_or_init ( || {
69
+ Postorder :: new ( & self . basic_blocks , START_BLOCK ) . map ( |( bb, _) | bb) . collect ( )
70
+ } )
53
71
}
54
72
55
73
/// `switch_sources()[&(target, switch)]` returns a list of switch
56
74
/// values that lead to a `target` block from a `switch` block.
57
75
#[ inline]
58
76
pub fn switch_sources ( & self ) -> & SwitchSources {
59
- self . switch_source_cache . compute ( & self . basic_blocks )
77
+ self . cache . switch_sources . get_or_init ( || {
78
+ let mut switch_sources: SwitchSources = FxHashMap :: default ( ) ;
79
+ for ( bb, data) in self . basic_blocks . iter_enumerated ( ) {
80
+ if let Some ( Terminator {
81
+ kind : TerminatorKind :: SwitchInt { targets, .. } , ..
82
+ } ) = & data. terminator
83
+ {
84
+ for ( value, target) in targets. iter ( ) {
85
+ switch_sources. entry ( ( target, bb) ) . or_default ( ) . push ( Some ( value) ) ;
86
+ }
87
+ switch_sources. entry ( ( targets. otherwise ( ) , bb) ) . or_default ( ) . push ( None ) ;
88
+ }
89
+ }
90
+ switch_sources
91
+ } )
60
92
}
61
93
62
94
/// Returns mutable reference to basic blocks. Invalidates CFG cache.
@@ -88,10 +120,7 @@ impl<'tcx> BasicBlocks<'tcx> {
88
120
/// All other methods that allow you to mutate the basic blocks also call this method
89
121
/// themselves, thereby avoiding any risk of accidentally cache invalidation.
90
122
pub fn invalidate_cfg_cache ( & mut self ) {
91
- self . predecessor_cache . invalidate ( ) ;
92
- self . switch_source_cache . invalidate ( ) ;
93
- self . is_cyclic . invalidate ( ) ;
94
- self . postorder_cache . invalidate ( ) ;
123
+ self . cache = Cache :: default ( ) ;
95
124
}
96
125
}
97
126
@@ -145,3 +174,24 @@ impl<'tcx> graph::WithPredecessors for BasicBlocks<'tcx> {
145
174
self . predecessors ( ) [ node] . iter ( ) . copied ( )
146
175
}
147
176
}
177
+
178
+ TrivialTypeTraversalAndLiftImpls ! {
179
+ Cache ,
180
+ }
181
+
182
+ impl < S : Encoder > Encodable < S > for Cache {
183
+ #[ inline]
184
+ fn encode ( & self , _s : & mut S ) { }
185
+ }
186
+
187
+ impl < D : Decoder > Decodable < D > for Cache {
188
+ #[ inline]
189
+ fn decode ( _: & mut D ) -> Self {
190
+ Default :: default ( )
191
+ }
192
+ }
193
+
194
+ impl < CTX > HashStable < CTX > for Cache {
195
+ #[ inline]
196
+ fn hash_stable ( & self , _: & mut CTX , _: & mut StableHasher ) { }
197
+ }
0 commit comments