@@ -245,7 +245,7 @@ pub fn reachable<'a, 'tcx>(
245245/// Returns a `BitSet` containing all basic blocks reachable from the `START_BLOCK`.
246246pub fn reachable_as_bitset ( body : & Body < ' _ > ) -> BitSet < BasicBlock > {
247247 let mut iter = preorder ( body) ;
248- iter . by_ref ( ) . for_each ( drop ) ;
248+ while let Some ( _ ) = iter . next ( ) { }
249249 iter. visited
250250}
251251
@@ -279,3 +279,97 @@ pub fn reverse_postorder<'a, 'tcx>(
279279{
280280 body. basic_blocks . reverse_postorder ( ) . iter ( ) . map ( |& bb| ( bb, & body. basic_blocks [ bb] ) )
281281}
282+
283+ /// Traversal of a [`Body`] that tries to avoid unreachable blocks in a monomorphized [`Instance`].
284+ ///
285+ /// This is allowed to have false positives; blocks may be visited even if they are not actually
286+ /// reachable.
287+ ///
288+ /// Such a traversal is mostly useful because it lets us skip lowering the `false` side
289+ /// of `if <T as Trait>::CONST`, as well as [`NullOp::UbChecks`].
290+ ///
291+ /// [`NullOp::UbChecks`]: rustc_middle::mir::NullOp::UbChecks
292+ pub fn mono_reachable < ' a , ' tcx > (
293+ body : & ' a Body < ' tcx > ,
294+ tcx : TyCtxt < ' tcx > ,
295+ instance : Instance < ' tcx > ,
296+ ) -> MonoReachable < ' a , ' tcx > {
297+ MonoReachable :: new ( body, tcx, instance)
298+ }
299+
300+ /// [`MonoReachable`] internally accumulates a [`BitSet`] of visited blocks. This is just a
301+ /// convenience function to run that traversal then extract its set of reached blocks.
302+ pub fn mono_reachable_as_bitset < ' a , ' tcx > (
303+ body : & ' a Body < ' tcx > ,
304+ tcx : TyCtxt < ' tcx > ,
305+ instance : Instance < ' tcx > ,
306+ ) -> BitSet < BasicBlock > {
307+ let mut iter = mono_reachable ( body, tcx, instance) ;
308+ while let Some ( _) = iter. next ( ) { }
309+ iter. visited
310+ }
311+
312+ pub struct MonoReachable < ' a , ' tcx > {
313+ body : & ' a Body < ' tcx > ,
314+ tcx : TyCtxt < ' tcx > ,
315+ instance : Instance < ' tcx > ,
316+ visited : BitSet < BasicBlock > ,
317+ // Other traversers track their worklist in a Vec. But we don't care about order, so we can
318+ // store ours in a BitSet and thus save allocations because BitSet has a small size
319+ // optimization.
320+ worklist : BitSet < BasicBlock > ,
321+ }
322+
323+ impl < ' a , ' tcx > MonoReachable < ' a , ' tcx > {
324+ pub fn new (
325+ body : & ' a Body < ' tcx > ,
326+ tcx : TyCtxt < ' tcx > ,
327+ instance : Instance < ' tcx > ,
328+ ) -> MonoReachable < ' a , ' tcx > {
329+ let mut worklist = BitSet :: new_empty ( body. basic_blocks . len ( ) ) ;
330+ worklist. insert ( START_BLOCK ) ;
331+ MonoReachable {
332+ body,
333+ tcx,
334+ instance,
335+ visited : BitSet :: new_empty ( body. basic_blocks . len ( ) ) ,
336+ worklist,
337+ }
338+ }
339+
340+ fn add_work ( & mut self , blocks : impl IntoIterator < Item = BasicBlock > ) {
341+ for block in blocks. into_iter ( ) {
342+ if !self . visited . contains ( block) {
343+ self . worklist . insert ( block) ;
344+ }
345+ }
346+ }
347+ }
348+
349+ impl < ' a , ' tcx > Iterator for MonoReachable < ' a , ' tcx > {
350+ type Item = ( BasicBlock , & ' a BasicBlockData < ' tcx > ) ;
351+
352+ fn next ( & mut self ) -> Option < ( BasicBlock , & ' a BasicBlockData < ' tcx > ) > {
353+ while let Some ( idx) = self . worklist . iter ( ) . next ( ) {
354+ self . worklist . remove ( idx) ;
355+ if !self . visited . insert ( idx) {
356+ continue ;
357+ }
358+
359+ let data = & self . body [ idx] ;
360+
361+ if let Some ( ( bits, targets) ) =
362+ Body :: try_const_mono_switchint ( self . tcx , self . instance , data)
363+ {
364+ let target = targets. target_for_value ( bits) ;
365+ self . add_work ( [ target] ) ;
366+ } else {
367+ self . add_work ( data. terminator ( ) . successors ( ) ) ;
368+ }
369+
370+ return Some ( ( idx, data) ) ;
371+ }
372+
373+ None
374+ }
375+ }
0 commit comments