1
1
//! A framework that can express both [gen-kill] and generic dataflow problems.
2
2
//!
3
- //! To use this framework, implement either the [`Analysis`] or the
4
- //! [`GenKillAnalysis`] trait. If your transfer function can be expressed with only gen/kill
5
- //! operations, prefer `GenKillAnalysis` since it will run faster while iterating to fixpoint. The
6
- //! `impls` module contains several examples of gen/kill dataflow analyses.
3
+ //! To use this framework, implement the [`Analysis`] trait. There used to be a `GenKillAnalysis`
4
+ //! alternative trait for gen-kill analyses that would pre-compute the transfer function for each
5
+ //! block. It was intended as an optimization, but it ended up not being any faster than
6
+ //! `Analysis`.
7
+ //!
8
+ //! The `impls` module contains several examples of dataflow analyses.
7
9
//!
8
10
//! Create an `Engine` for your analysis using the `into_engine` method on the `Analysis` trait,
9
11
//! then call `iterate_to_fixpoint`. From there, you can use a `ResultsCursor` to inspect the
@@ -122,9 +124,9 @@ pub trait AnalysisDomain<'tcx> {
122
124
///
123
125
/// # Convergence
124
126
///
125
- /// When implementing this trait directly (not via [`GenKillAnalysis`]), it's possible to choose a
126
- /// transfer function such that the analysis does not reach fixpoint. To guarantee convergence,
127
- /// your transfer functions must maintain the following invariant:
127
+ /// When implementing this trait it's possible to choose a transfer function such that the analysis
128
+ /// does not reach fixpoint. To guarantee convergence, your transfer functions must maintain the
129
+ /// following invariant:
128
130
///
129
131
/// > If the dataflow state **before** some point in the program changes to be greater
130
132
/// than the prior state **before** that point, the dataflow state **after** that point must
@@ -223,9 +225,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
223
225
224
226
/// Creates an `Engine` to find the fixpoint for this dataflow problem.
225
227
///
226
- /// You shouldn't need to override this outside this module, since the combination of the
227
- /// default impl and the one for all `A: GenKillAnalysis` will do the right thing.
228
- /// Its purpose is to enable method chaining like so:
228
+ /// You shouldn't need to override this. Its purpose is to enable method chaining like so:
229
229
///
230
230
/// ```ignore (cross-crate-imports)
231
231
/// let results = MyAnalysis::new(tcx, body)
@@ -246,146 +246,7 @@ pub trait Analysis<'tcx>: AnalysisDomain<'tcx> {
246
246
}
247
247
}
248
248
249
- /// A gen/kill dataflow problem.
250
- ///
251
- /// Each method in this trait has a corresponding one in `Analysis`. However, the first two methods
252
- /// here only allow modification of the dataflow state via "gen" and "kill" operations. By defining
253
- /// transfer functions for each statement in this way, the transfer function for an entire basic
254
- /// block can be computed efficiently. The remaining methods match up with `Analysis` exactly.
255
- ///
256
- /// `Analysis` is automatically implemented for all implementers of `GenKillAnalysis` via a blanket
257
- /// impl below.
258
- pub trait GenKillAnalysis < ' tcx > : Analysis < ' tcx > {
259
- type Idx : Idx ;
260
-
261
- fn domain_size ( & self , body : & mir:: Body < ' tcx > ) -> usize ;
262
-
263
- /// See `Analysis::apply_statement_effect`.
264
- fn statement_effect (
265
- & mut self ,
266
- trans : & mut Self :: Domain ,
267
- statement : & mir:: Statement < ' tcx > ,
268
- location : Location ,
269
- ) ;
270
-
271
- /// See `Analysis::apply_before_statement_effect`.
272
- fn before_statement_effect (
273
- & mut self ,
274
- _trans : & mut Self :: Domain ,
275
- _statement : & mir:: Statement < ' tcx > ,
276
- _location : Location ,
277
- ) {
278
- }
279
-
280
- /// See `Analysis::apply_terminator_effect`.
281
- fn terminator_effect < ' mir > (
282
- & mut self ,
283
- trans : & mut Self :: Domain ,
284
- terminator : & ' mir mir:: Terminator < ' tcx > ,
285
- location : Location ,
286
- ) -> TerminatorEdges < ' mir , ' tcx > ;
287
-
288
- /// See `Analysis::apply_before_terminator_effect`.
289
- fn before_terminator_effect (
290
- & mut self ,
291
- _trans : & mut Self :: Domain ,
292
- _terminator : & mir:: Terminator < ' tcx > ,
293
- _location : Location ,
294
- ) {
295
- }
296
-
297
- /* Edge-specific effects */
298
-
299
- /// See `Analysis::apply_call_return_effect`.
300
- fn call_return_effect (
301
- & mut self ,
302
- trans : & mut Self :: Domain ,
303
- block : BasicBlock ,
304
- return_places : CallReturnPlaces < ' _ , ' tcx > ,
305
- ) ;
306
-
307
- /// See `Analysis::apply_switch_int_edge_effects`.
308
- fn switch_int_edge_effects (
309
- & mut self ,
310
- _block : BasicBlock ,
311
- _discr : & mir:: Operand < ' tcx > ,
312
- _edge_effects : & mut impl SwitchIntEdgeEffects < Self :: Domain > ,
313
- ) {
314
- }
315
- }
316
-
317
- // Blanket impl: any impl of `GenKillAnalysis` automatically impls `Analysis`.
318
- impl < ' tcx , A > Analysis < ' tcx > for A
319
- where
320
- A : GenKillAnalysis < ' tcx > ,
321
- A :: Domain : GenKill < A :: Idx > + BitSetExt < A :: Idx > ,
322
- {
323
- fn apply_statement_effect (
324
- & mut self ,
325
- state : & mut A :: Domain ,
326
- statement : & mir:: Statement < ' tcx > ,
327
- location : Location ,
328
- ) {
329
- self . statement_effect ( state, statement, location) ;
330
- }
331
-
332
- fn apply_before_statement_effect (
333
- & mut self ,
334
- state : & mut A :: Domain ,
335
- statement : & mir:: Statement < ' tcx > ,
336
- location : Location ,
337
- ) {
338
- self . before_statement_effect ( state, statement, location) ;
339
- }
340
-
341
- fn apply_terminator_effect < ' mir > (
342
- & mut self ,
343
- state : & mut A :: Domain ,
344
- terminator : & ' mir mir:: Terminator < ' tcx > ,
345
- location : Location ,
346
- ) -> TerminatorEdges < ' mir , ' tcx > {
347
- self . terminator_effect ( state, terminator, location)
348
- }
349
-
350
- fn apply_before_terminator_effect (
351
- & mut self ,
352
- state : & mut A :: Domain ,
353
- terminator : & mir:: Terminator < ' tcx > ,
354
- location : Location ,
355
- ) {
356
- self . before_terminator_effect ( state, terminator, location) ;
357
- }
358
-
359
- /* Edge-specific effects */
360
-
361
- fn apply_call_return_effect (
362
- & mut self ,
363
- state : & mut A :: Domain ,
364
- block : BasicBlock ,
365
- return_places : CallReturnPlaces < ' _ , ' tcx > ,
366
- ) {
367
- self . call_return_effect ( state, block, return_places) ;
368
- }
369
-
370
- fn apply_switch_int_edge_effects (
371
- & mut self ,
372
- block : BasicBlock ,
373
- discr : & mir:: Operand < ' tcx > ,
374
- edge_effects : & mut impl SwitchIntEdgeEffects < A :: Domain > ,
375
- ) {
376
- self . switch_int_edge_effects ( block, discr, edge_effects) ;
377
- }
378
- }
379
-
380
249
/// The legal operations for a transfer function in a gen/kill problem.
381
- ///
382
- /// This abstraction exists because there are two different contexts in which we call the methods in
383
- /// `GenKillAnalysis`. Sometimes we need to store a single transfer function that can be efficiently
384
- /// applied multiple times, such as when computing the cumulative transfer function for each block.
385
- /// These cases require a `GenKillSet`, which in turn requires two `BitSet`s of storage. Oftentimes,
386
- /// however, we only need to apply an effect once. In *these* cases, it is more efficient to pass the
387
- /// `BitSet` representing the state vector directly into the `*_effect` methods as opposed to
388
- /// building up a `GenKillSet` and then throwing it away.
389
250
pub trait GenKill < T > {
390
251
/// Inserts `elem` into the state vector.
391
252
fn gen_ ( & mut self , elem : T ) ;
0 commit comments