@@ -21,15 +21,15 @@ pub struct Engine<'a, 'tcx, A>
21
21
where
22
22
A : Analysis < ' tcx > ,
23
23
{
24
- bits_per_block : usize ,
25
24
tcx : TyCtxt < ' tcx > ,
26
25
body : & ' a mir:: Body < ' tcx > ,
27
26
def_id : DefId ,
28
27
dead_unwinds : Option < & ' a BitSet < BasicBlock > > ,
29
- entry_sets : IndexVec < BasicBlock , BitSet < A :: Idx > > ,
30
28
analysis : A ,
31
29
32
30
/// Cached, cumulative transfer functions for each block.
31
+ ///
32
+ /// These are only computable for gen-kill problems.
33
33
trans_for_block : Option < IndexVec < BasicBlock , GenKillSet < A :: Idx > > > ,
34
34
}
35
35
@@ -98,25 +98,12 @@ where
98
98
analysis : A ,
99
99
trans_for_block : Option < IndexVec < BasicBlock , GenKillSet < A :: Idx > > > ,
100
100
) -> Self {
101
- let bits_per_block = analysis. bits_per_block ( body) ;
102
-
103
- let bottom_value_set = if A :: BOTTOM_VALUE == true {
104
- BitSet :: new_filled ( bits_per_block)
105
- } else {
106
- BitSet :: new_empty ( bits_per_block)
107
- } ;
108
-
109
- let mut entry_sets = IndexVec :: from_elem ( bottom_value_set, body. basic_blocks ( ) ) ;
110
- analysis. initialize_start_block ( body, & mut entry_sets[ mir:: START_BLOCK ] ) ;
111
-
112
101
Engine {
113
102
analysis,
114
- bits_per_block,
115
103
tcx,
116
104
body,
117
105
def_id,
118
106
dead_unwinds : None ,
119
- entry_sets,
120
107
trans_for_block,
121
108
}
122
109
}
@@ -126,37 +113,39 @@ where
126
113
self
127
114
}
128
115
129
- pub fn iterate_to_fixpoint ( mut self ) -> Results < ' tcx , A > {
130
- let mut temp_state = BitSet :: new_empty ( self . bits_per_block ) ;
131
-
132
- let mut dirty_queue: WorkQueue < BasicBlock > =
133
- WorkQueue :: with_none ( self . body . basic_blocks ( ) . len ( ) ) ;
134
-
135
- for ( bb, _) in traversal:: reverse_postorder ( self . body ) {
136
- dirty_queue. insert ( bb) ;
137
- }
138
-
139
- // Add blocks that are not reachable from START_BLOCK to the work queue. These blocks will
140
- // be processed after the ones added above.
141
- for bb in self . body . basic_blocks ( ) . indices ( ) {
142
- dirty_queue. insert ( bb) ;
143
- }
116
+ pub fn iterate_to_fixpoint ( self ) -> Results < ' tcx , A > {
117
+ // Initialize the entry sets for each block.
144
118
145
- while let Some ( bb) = dirty_queue. pop ( ) {
146
- let bb_data = & self . body [ bb] ;
147
- let on_entry = & self . entry_sets [ bb] ;
119
+ let bits_per_block = self . analysis . bits_per_block ( self . body ) ;
120
+ let bottom_value_set = if A :: BOTTOM_VALUE == true {
121
+ BitSet :: new_filled ( bits_per_block)
122
+ } else {
123
+ BitSet :: new_empty ( bits_per_block)
124
+ } ;
148
125
149
- temp_state . overwrite ( on_entry ) ;
150
- self . apply_whole_block_effect ( & mut temp_state , bb , bb_data ) ;
126
+ let mut entry_sets = IndexVec :: from_elem ( bottom_value_set , self . body . basic_blocks ( ) ) ;
127
+ self . analysis . initialize_start_block ( self . body , & mut entry_sets [ mir :: START_BLOCK ] ) ;
151
128
152
- self . propagate_bits_into_graph_successors_of (
153
- & mut temp_state,
154
- ( bb, bb_data) ,
155
- & mut dirty_queue,
129
+ // To improve performance, we check for the existence of cached block transfer functions
130
+ // *outside* the loop in `_iterate_to_fixpoint` below.
131
+ if let Some ( trans_for_block) = & self . trans_for_block {
132
+ self . _iterate_to_fixpoint (
133
+ bits_per_block,
134
+ & mut entry_sets,
135
+ |state, bb| trans_for_block[ bb] . apply ( state) ,
136
+ ) ;
137
+ } else {
138
+ self . _iterate_to_fixpoint (
139
+ bits_per_block,
140
+ & mut entry_sets,
141
+ |state, bb| {
142
+ let block_data = & self . body [ bb] ;
143
+ apply_whole_block_effect ( & self . analysis , state, bb, block_data) ;
144
+ }
156
145
) ;
157
146
}
158
147
159
- let Engine { tcx, body , def_id, trans_for_block , entry_sets , analysis , .. } = self ;
148
+ let Engine { tcx, def_id, body , analysis , trans_for_block , .. } = self ;
160
149
let results = Results { analysis, entry_sets } ;
161
150
162
151
let res = write_graphviz_results ( tcx, def_id, body, & results, trans_for_block) ;
@@ -167,124 +156,155 @@ where
167
156
results
168
157
}
169
158
170
- /// Applies the cumulative effect of an entire block, excluding the call return effect if one
171
- /// exists .
172
- fn apply_whole_block_effect (
159
+ /// Helper function that propagates dataflow state into graph succesors until fixpoint is
160
+ /// reached .
161
+ fn _iterate_to_fixpoint (
173
162
& self ,
174
- state : & mut BitSet < A :: Idx > ,
175
- block : BasicBlock ,
176
- block_data : & mir :: BasicBlockData < ' tcx > ,
163
+ bits_per_block : usize ,
164
+ entry_sets : & mut IndexVec < BasicBlock , BitSet < A :: Idx > > ,
165
+ apply_block_effect : impl Fn ( & mut BitSet < A :: Idx > , BasicBlock ) ,
177
166
) {
178
- // Use the cached block transfer function if available.
179
- if let Some ( trans_for_block) = & self . trans_for_block {
180
- trans_for_block[ block] . apply ( state) ;
181
- return ;
167
+ let body = self . body ;
168
+ let mut state = BitSet :: new_empty ( bits_per_block) ;
169
+
170
+ let mut dirty_queue: WorkQueue < BasicBlock > =
171
+ WorkQueue :: with_none ( body. basic_blocks ( ) . len ( ) ) ;
172
+
173
+ for ( bb, _) in traversal:: reverse_postorder ( body) {
174
+ dirty_queue. insert ( bb) ;
182
175
}
183
176
184
- // Otherwise apply effects one-by-one.
177
+ // Add blocks that are not reachable from START_BLOCK to the work queue. These blocks will
178
+ // be processed after the ones added above.
179
+ for bb in body. basic_blocks ( ) . indices ( ) {
180
+ dirty_queue. insert ( bb) ;
181
+ }
182
+
183
+ while let Some ( bb) = dirty_queue. pop ( ) {
184
+ state. overwrite ( & entry_sets[ bb] ) ;
185
+ apply_block_effect ( & mut state, bb) ;
185
186
186
- for ( statement_index, statement) in block_data. statements . iter ( ) . enumerate ( ) {
187
- let location = Location { block, statement_index } ;
188
- self . analysis . apply_before_statement_effect ( state, statement, location) ;
189
- self . analysis . apply_statement_effect ( state, statement, location) ;
187
+ self . propagate_bits_into_graph_successors_of (
188
+ entry_sets,
189
+ & mut state,
190
+ ( bb, & body[ bb] ) ,
191
+ & mut dirty_queue,
192
+ ) ;
190
193
}
194
+ }
191
195
192
- let terminator = block_data. terminator ( ) ;
193
- let location = Location { block, statement_index : block_data. statements . len ( ) } ;
194
- self . analysis . apply_before_terminator_effect ( state, terminator, location) ;
195
- self . analysis . apply_terminator_effect ( state, terminator, location) ;
196
+ fn propagate_state_to (
197
+ & self ,
198
+ bb : BasicBlock ,
199
+ state : & BitSet < A :: Idx > ,
200
+ entry_sets : & mut IndexVec < BasicBlock , BitSet < A :: Idx > > ,
201
+ dirty_queue : & mut WorkQueue < BasicBlock > ,
202
+ ) {
203
+ let entry_set = & mut entry_sets[ bb] ;
204
+ let set_changed = self . analysis . join ( entry_set, state) ;
205
+ if set_changed {
206
+ dirty_queue. insert ( bb) ;
207
+ }
196
208
}
197
209
198
210
fn propagate_bits_into_graph_successors_of (
199
- & mut self ,
200
- in_out : & mut BitSet < A :: Idx > ,
201
- ( bb, bb_data) : ( BasicBlock , & ' a mir:: BasicBlockData < ' tcx > ) ,
202
- dirty_list : & mut WorkQueue < BasicBlock > ,
211
+ & self ,
212
+ entry_sets : & mut IndexVec < BasicBlock , BitSet < A :: Idx > > ,
213
+ exit_state : & mut BitSet < A :: Idx > ,
214
+ ( bb, bb_data) : ( BasicBlock , & mir:: BasicBlockData < ' tcx > ) ,
215
+ dirty : & mut WorkQueue < BasicBlock > ,
203
216
) {
204
- use mir:: TerminatorKind :: * ;
205
-
206
- // FIXME: This should be implemented using a `for_each_successor` method on
207
- // `TerminatorKind`.
217
+ use mir:: TerminatorKind ;
208
218
209
219
match bb_data. terminator ( ) . kind {
210
- | Return
211
- | Resume
212
- | Abort
213
- | GeneratorDrop
214
- | Unreachable
220
+ | TerminatorKind :: Return
221
+ | TerminatorKind :: Resume
222
+ | TerminatorKind :: Abort
223
+ | TerminatorKind :: GeneratorDrop
224
+ | TerminatorKind :: Unreachable
215
225
=> { }
216
226
217
- | Goto { target }
218
- | Assert { target, cleanup : None , .. }
219
- | Yield { resume : target, drop : None , .. }
220
- | Drop { target, location : _, unwind : None }
221
- | DropAndReplace { target, value : _, location : _, unwind : None }
222
- => self . propagate_bits_into_entry_set_for ( in_out , target , dirty_list ) ,
227
+ | TerminatorKind :: Goto { target }
228
+ | TerminatorKind :: Assert { target, cleanup : None , .. }
229
+ | TerminatorKind :: Yield { resume : target, drop : None , .. }
230
+ | TerminatorKind :: Drop { target, location : _, unwind : None }
231
+ | TerminatorKind :: DropAndReplace { target, value : _, location : _, unwind : None }
232
+ => self . propagate_state_to ( target , exit_state , entry_sets , dirty ) ,
223
233
224
- Yield { resume : target, drop : Some ( drop) , .. } => {
225
- self . propagate_bits_into_entry_set_for ( in_out , target , dirty_list ) ;
226
- self . propagate_bits_into_entry_set_for ( in_out , drop , dirty_list ) ;
234
+ TerminatorKind :: Yield { resume : target, drop : Some ( drop) , .. } => {
235
+ self . propagate_state_to ( target , exit_state , entry_sets , dirty ) ;
236
+ self . propagate_state_to ( drop , exit_state , entry_sets , dirty ) ;
227
237
}
228
238
229
- | Assert { target, cleanup : Some ( unwind) , .. }
230
- | Drop { target, location : _, unwind : Some ( unwind) }
231
- | DropAndReplace { target, value : _, location : _, unwind : Some ( unwind) }
239
+ | TerminatorKind :: Assert { target, cleanup : Some ( unwind) , .. }
240
+ | TerminatorKind :: Drop { target, location : _, unwind : Some ( unwind) }
241
+ | TerminatorKind :: DropAndReplace { target, value : _, location : _, unwind : Some ( unwind) }
232
242
=> {
233
- self . propagate_bits_into_entry_set_for ( in_out , target , dirty_list ) ;
243
+ self . propagate_state_to ( target , exit_state , entry_sets , dirty ) ;
234
244
if self . dead_unwinds . map_or ( true , |bbs| !bbs. contains ( bb) ) {
235
- self . propagate_bits_into_entry_set_for ( in_out , unwind , dirty_list ) ;
245
+ self . propagate_state_to ( unwind , exit_state , entry_sets , dirty ) ;
236
246
}
237
247
}
238
248
239
- SwitchInt { ref targets, .. } => {
249
+ TerminatorKind :: SwitchInt { ref targets, .. } => {
240
250
for target in targets {
241
- self . propagate_bits_into_entry_set_for ( in_out , * target, dirty_list ) ;
251
+ self . propagate_state_to ( * target, exit_state , entry_sets , dirty ) ;
242
252
}
243
253
}
244
254
245
- Call { cleanup, ref destination, ref func, ref args, .. } => {
255
+ TerminatorKind :: Call { cleanup, ref destination, ref func, ref args, .. } => {
246
256
if let Some ( unwind) = cleanup {
247
257
if self . dead_unwinds . map_or ( true , |bbs| !bbs. contains ( bb) ) {
248
- self . propagate_bits_into_entry_set_for ( in_out , unwind , dirty_list ) ;
258
+ self . propagate_state_to ( unwind , exit_state , entry_sets , dirty ) ;
249
259
}
250
260
}
251
261
252
262
if let Some ( ( ref dest_place, dest_bb) ) = * destination {
253
263
// N.B.: This must be done *last*, otherwise the unwind path will see the call
254
264
// return effect.
255
- self . analysis . apply_call_return_effect ( in_out , bb, func, args, dest_place) ;
256
- self . propagate_bits_into_entry_set_for ( in_out , dest_bb , dirty_list ) ;
265
+ self . analysis . apply_call_return_effect ( exit_state , bb, func, args, dest_place) ;
266
+ self . propagate_state_to ( dest_bb , exit_state , entry_sets , dirty ) ;
257
267
}
258
268
}
259
269
260
- FalseEdges { real_target, imaginary_target } => {
261
- self . propagate_bits_into_entry_set_for ( in_out , real_target , dirty_list ) ;
262
- self . propagate_bits_into_entry_set_for ( in_out , imaginary_target , dirty_list ) ;
270
+ TerminatorKind :: FalseEdges { real_target, imaginary_target } => {
271
+ self . propagate_state_to ( real_target , exit_state , entry_sets , dirty ) ;
272
+ self . propagate_state_to ( imaginary_target , exit_state , entry_sets , dirty ) ;
263
273
}
264
274
265
- FalseUnwind { real_target, unwind } => {
266
- self . propagate_bits_into_entry_set_for ( in_out , real_target , dirty_list ) ;
275
+ TerminatorKind :: FalseUnwind { real_target, unwind } => {
276
+ self . propagate_state_to ( real_target , exit_state , entry_sets , dirty ) ;
267
277
if let Some ( unwind) = unwind {
268
278
if self . dead_unwinds . map_or ( true , |bbs| !bbs. contains ( bb) ) {
269
- self . propagate_bits_into_entry_set_for ( in_out , unwind , dirty_list ) ;
279
+ self . propagate_state_to ( unwind , exit_state , entry_sets , dirty ) ;
270
280
}
271
281
}
272
282
}
273
283
}
274
284
}
285
+ }
275
286
276
- fn propagate_bits_into_entry_set_for (
277
- & mut self ,
278
- in_out : & BitSet < A :: Idx > ,
279
- bb : BasicBlock ,
280
- dirty_queue : & mut WorkQueue < BasicBlock > ,
281
- ) {
282
- let entry_set = & mut self . entry_sets [ bb] ;
283
- let set_changed = self . analysis . join ( entry_set, & in_out) ;
284
- if set_changed {
285
- dirty_queue. insert ( bb) ;
286
- }
287
+ /// Applies the cumulative effect of an entire block, excluding the call return effect if one
288
+ /// exists.
289
+ fn apply_whole_block_effect < A > (
290
+ analysis : & ' a A ,
291
+ state : & mut BitSet < A :: Idx > ,
292
+ block : BasicBlock ,
293
+ block_data : & ' a mir:: BasicBlockData < ' tcx > ,
294
+ )
295
+ where
296
+ A : Analysis < ' tcx > ,
297
+ {
298
+ for ( statement_index, statement) in block_data. statements . iter ( ) . enumerate ( ) {
299
+ let location = Location { block, statement_index } ;
300
+ analysis. apply_before_statement_effect ( state, statement, location) ;
301
+ analysis. apply_statement_effect ( state, statement, location) ;
287
302
}
303
+
304
+ let terminator = block_data. terminator ( ) ;
305
+ let location = Location { block, statement_index : block_data. statements . len ( ) } ;
306
+ analysis. apply_before_terminator_effect ( state, terminator, location) ;
307
+ analysis. apply_terminator_effect ( state, terminator, location) ;
288
308
}
289
309
290
310
// Graphviz
0 commit comments