7171 }
7272}
7373
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+
7481/// A solver for dataflow problems.
7582pub struct Engine < ' a , ' tcx , A >
7683where
9097 // performance in practice. I've tried a few ways to avoid this, but they have downsides. See
9198 // the message for the commit that added this FIXME for more information.
9299 apply_trans_for_block : Option < Box < dyn Fn ( BasicBlock , & mut A :: Domain ) > > ,
100+ blocks : Blocks ,
93101}
94102
95103impl < ' a , ' tcx , A , D , T > Engine < ' a , ' tcx , A >
@@ -99,13 +107,18 @@ where
99107 T : Idx ,
100108{
101109 /// 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 {
103116 // If there are no back-edges in the control-flow graph, we only ever need to apply the
104117 // transfer function for each block exactly once (assuming that we process blocks in RPO).
105118 //
106119 // In this case, there's no need to compute the block transfer functions ahead of time.
107120 if !body. basic_blocks . is_cfg_cyclic ( ) {
108- return Self :: new ( tcx, body, analysis, None ) ;
121+ return Self :: new ( tcx, body, analysis, None , blocks ) ;
109122 }
110123
111124 // Otherwise, compute and store the cumulative transfer function for each block.
@@ -122,7 +135,7 @@ where
122135 trans_for_block[ bb] . apply ( state) ;
123136 } ) ;
124137
125- Self :: new ( tcx, body, analysis, Some ( apply_trans as Box < _ > ) )
138+ Self :: new ( tcx, body, analysis, Some ( apply_trans as Box < _ > ) , blocks )
126139 }
127140}
128141
@@ -136,15 +149,21 @@ where
136149 ///
137150 /// Gen-kill problems should use `new_gen_kill`, which will coalesce transfer functions for
138151 /// 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)
141159 }
142160
143161 fn new (
144162 tcx : TyCtxt < ' tcx > ,
145163 body : & ' a mir:: Body < ' tcx > ,
146164 analysis : A ,
147165 apply_trans_for_block : Option < Box < dyn Fn ( BasicBlock , & mut A :: Domain ) > > ,
166+ blocks : Blocks ,
148167 ) -> Self {
149168 let bottom_value = analysis. bottom_value ( body) ;
150169 let mut entry_sets = IndexVec :: from_elem ( bottom_value. clone ( ) , & body. basic_blocks ) ;
@@ -162,6 +181,7 @@ where
162181 pass_name : None ,
163182 entry_sets,
164183 apply_trans_for_block,
184+ blocks,
165185 }
166186 }
167187
@@ -197,21 +217,32 @@ where
197217 tcx,
198218 apply_trans_for_block,
199219 pass_name,
220+ blocks,
200221 ..
201222 } = self ;
202223
203224 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 ( ) ) ;
204227
205228 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+ }
208238 }
209239 } else {
210240 // Reverse post-order on the reverse CFG may generate a better iteration order for
211241 // backward dataflow analyses, but probably not enough to matter.
212242 for ( bb, _) in traversal:: postorder ( body) {
213243 dirty_queue. insert ( bb) ;
214244 }
245+ assert ! ( matches!( blocks, Blocks :: All ) ) ;
215246 }
216247
217248 // `state` is not actually used between iterations;
@@ -241,8 +272,9 @@ where
241272 ( bb, bb_data) ,
242273 |target : BasicBlock , state : & A :: Domain | {
243274 let set_changed = entry_sets[ target] . join ( state) ;
244- if set_changed {
275+ if set_changed || !visited . contains ( target ) {
245276 dirty_queue. insert ( target) ;
277+ visited. insert ( target) ;
246278 }
247279 } ,
248280 ) ;
0 commit comments