71
71
}
72
72
}
73
73
74
+ pub enum Blocks {
75
+ /// Consider all MIR blocks
76
+ All ,
77
+ /// Consider only the MIR blocks reachable from the start
78
+ Reachable ,
79
+ }
80
+
74
81
/// A solver for dataflow problems.
75
82
pub struct Engine < ' a , ' tcx , A >
76
83
where
90
97
// performance in practice. I've tried a few ways to avoid this, but they have downsides. See
91
98
// the message for the commit that added this FIXME for more information.
92
99
apply_trans_for_block : Option < Box < dyn Fn ( BasicBlock , & mut A :: Domain ) > > ,
100
+ blocks : Blocks ,
93
101
}
94
102
95
103
impl < ' a , ' tcx , A , D , T > Engine < ' a , ' tcx , A >
@@ -99,13 +107,18 @@ where
99
107
T : Idx ,
100
108
{
101
109
/// Creates a new `Engine` to solve a gen-kill dataflow problem.
102
- pub fn new_gen_kill ( tcx : TyCtxt < ' tcx > , body : & ' a mir:: Body < ' tcx > , analysis : A ) -> Self {
110
+ pub fn new_gen_kill (
111
+ tcx : TyCtxt < ' tcx > ,
112
+ body : & ' a mir:: Body < ' tcx > ,
113
+ analysis : A ,
114
+ blocks : Blocks ,
115
+ ) -> Self {
103
116
// If there are no back-edges in the control-flow graph, we only ever need to apply the
104
117
// transfer function for each block exactly once (assuming that we process blocks in RPO).
105
118
//
106
119
// In this case, there's no need to compute the block transfer functions ahead of time.
107
120
if !body. basic_blocks . is_cfg_cyclic ( ) {
108
- return Self :: new ( tcx, body, analysis, None ) ;
121
+ return Self :: new ( tcx, body, analysis, None , blocks ) ;
109
122
}
110
123
111
124
// Otherwise, compute and store the cumulative transfer function for each block.
@@ -122,7 +135,7 @@ where
122
135
trans_for_block[ bb] . apply ( state) ;
123
136
} ) ;
124
137
125
- Self :: new ( tcx, body, analysis, Some ( apply_trans as Box < _ > ) )
138
+ Self :: new ( tcx, body, analysis, Some ( apply_trans as Box < _ > ) , blocks )
126
139
}
127
140
}
128
141
@@ -136,15 +149,21 @@ where
136
149
///
137
150
/// Gen-kill problems should use `new_gen_kill`, which will coalesce transfer functions for
138
151
/// better performance.
139
- pub fn new_generic ( tcx : TyCtxt < ' tcx > , body : & ' a mir:: Body < ' tcx > , analysis : A ) -> Self {
140
- Self :: new ( tcx, body, analysis, None )
152
+ pub fn new_generic (
153
+ tcx : TyCtxt < ' tcx > ,
154
+ body : & ' a mir:: Body < ' tcx > ,
155
+ analysis : A ,
156
+ blocks : Blocks ,
157
+ ) -> Self {
158
+ Self :: new ( tcx, body, analysis, None , blocks)
141
159
}
142
160
143
161
fn new (
144
162
tcx : TyCtxt < ' tcx > ,
145
163
body : & ' a mir:: Body < ' tcx > ,
146
164
analysis : A ,
147
165
apply_trans_for_block : Option < Box < dyn Fn ( BasicBlock , & mut A :: Domain ) > > ,
166
+ blocks : Blocks ,
148
167
) -> Self {
149
168
let bottom_value = analysis. bottom_value ( body) ;
150
169
let mut entry_sets = IndexVec :: from_elem ( bottom_value. clone ( ) , & body. basic_blocks ) ;
@@ -162,6 +181,7 @@ where
162
181
pass_name : None ,
163
182
entry_sets,
164
183
apply_trans_for_block,
184
+ blocks,
165
185
}
166
186
}
167
187
@@ -197,21 +217,32 @@ where
197
217
tcx,
198
218
apply_trans_for_block,
199
219
pass_name,
220
+ blocks,
200
221
..
201
222
} = self ;
202
223
203
224
let mut dirty_queue: WorkQueue < BasicBlock > = WorkQueue :: with_none ( body. basic_blocks . len ( ) ) ;
225
+ // could be tracked in `entry_sets`
226
+ let mut visited = BitSet :: new_empty ( body. basic_blocks . len ( ) ) ;
204
227
205
228
if A :: Direction :: IS_FORWARD {
206
- for ( bb, _) in traversal:: reverse_postorder ( body) {
207
- dirty_queue. insert ( bb) ;
229
+ match blocks {
230
+ Blocks :: All => {
231
+ for ( bb, _) in traversal:: reverse_postorder ( body) {
232
+ dirty_queue. insert ( bb) ;
233
+ }
234
+ }
235
+ Blocks :: Reachable => {
236
+ dirty_queue. insert ( mir:: START_BLOCK ) ;
237
+ }
208
238
}
209
239
} else {
210
240
// Reverse post-order on the reverse CFG may generate a better iteration order for
211
241
// backward dataflow analyses, but probably not enough to matter.
212
242
for ( bb, _) in traversal:: postorder ( body) {
213
243
dirty_queue. insert ( bb) ;
214
244
}
245
+ assert ! ( matches!( blocks, Blocks :: All ) ) ;
215
246
}
216
247
217
248
// `state` is not actually used between iterations;
@@ -241,8 +272,9 @@ where
241
272
( bb, bb_data) ,
242
273
|target : BasicBlock , state : & A :: Domain | {
243
274
let set_changed = entry_sets[ target] . join ( state) ;
244
- if set_changed {
275
+ if set_changed || !visited . contains ( target ) {
245
276
dirty_queue. insert ( target) ;
277
+ visited. insert ( target) ;
246
278
}
247
279
} ,
248
280
) ;
0 commit comments