2626//! - bound the maximum depth by a constant `MAX_BACKTRACK`;
2727//! - we only traverse `Goto` terminators.
2828//!
29+ //! We try to avoid creating irreducible control-flow by not threading through a loop header.
30+ //!
2931//! Likewise, applying the optimisation can create a lot of new MIR, so we bound the instruction
3032//! cost by `MAX_COST`.
3133
3234use rustc_arena:: DroplessArena ;
3335use rustc_data_structures:: fx:: FxHashSet ;
36+ use rustc_index:: bit_set:: BitSet ;
3437use rustc_index:: IndexVec ;
3538use rustc_middle:: mir:: visit:: Visitor ;
3639use rustc_middle:: mir:: * ;
@@ -58,14 +61,22 @@ impl<'tcx> MirPass<'tcx> for JumpThreading {
5861
5962 let param_env = tcx. param_env_reveal_all_normalized ( def_id) ;
6063 let map = Map :: new ( tcx, body, Some ( MAX_PLACES ) ) ;
64+ let loop_headers = loop_headers ( body) ;
6165
6266 let arena = DroplessArena :: default ( ) ;
63- let mut finder =
64- TOFinder { tcx, param_env, body, arena : & arena, map : & map, opportunities : Vec :: new ( ) } ;
67+ let mut finder = TOFinder {
68+ tcx,
69+ param_env,
70+ body,
71+ arena : & arena,
72+ map : & map,
73+ loop_headers : & loop_headers,
74+ opportunities : Vec :: new ( ) ,
75+ } ;
6576
6677 for ( bb, bbdata) in body. basic_blocks . iter_enumerated ( ) {
6778 debug ! ( ?bb, term = ?bbdata. terminator( ) ) ;
68- if bbdata. is_cleanup {
79+ if bbdata. is_cleanup || loop_headers . contains ( bb ) {
6980 continue ;
7081 }
7182 let Some ( ( discr, targets) ) = bbdata. terminator ( ) . kind . as_switch ( ) else { continue } ;
@@ -108,6 +119,10 @@ impl<'tcx> MirPass<'tcx> for JumpThreading {
108119 return ;
109120 }
110121
122+ // Verify that we do not thread through a loop header.
123+ for to in opportunities. iter ( ) {
124+ assert ! ( to. chain. iter( ) . all( |& block| !loop_headers. contains( block) ) ) ;
125+ }
111126 OpportunitySet :: new ( body, opportunities) . apply ( body) ;
112127 }
113128}
@@ -125,6 +140,7 @@ struct TOFinder<'tcx, 'a> {
125140 param_env : ty:: ParamEnv < ' tcx > ,
126141 body : & ' a Body < ' tcx > ,
127142 map : & ' a Map ,
143+ loop_headers : & ' a BitSet < BasicBlock > ,
128144 /// We use an arena to avoid cloning the slices when cloning `state`.
129145 arena : & ' a DroplessArena ,
130146 opportunities : Vec < ThreadingOpportunity > ,
@@ -180,6 +196,11 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
180196 mut cost : CostChecker < ' _ , ' tcx > ,
181197 depth : usize ,
182198 ) {
199+ // Do not thread through loop headers.
200+ if self . loop_headers . contains ( bb) {
201+ return ;
202+ }
203+
183204 debug ! ( cost = ?cost. cost( ) ) ;
184205 for ( statement_index, stmt) in
185206 self . body . basic_blocks [ bb] . statements . iter ( ) . enumerate ( ) . rev ( )
@@ -636,3 +657,21 @@ enum Update {
636657 Incr ,
637658 Decr ,
638659}
660+
661+ /// Compute the set of loop headers in the given body. We define a loop header as a block which has
662+ /// at least a predecessor which it dominates. This definition is only correct for reducible CFGs.
663+ /// But if the CFG is already irreducible, there is no point in trying much harder.
664+ /// is already irreducibl
665+ fn loop_headers ( body : & Body < ' _ > ) -> BitSet < BasicBlock > {
666+ let mut loop_headers = BitSet :: new_empty ( body. basic_blocks . len ( ) ) ;
667+ let dominators = body. basic_blocks . dominators ( ) ;
668+ // Only visit reachable blocks.
669+ for ( bb, bbdata) in traversal:: preorder ( body) {
670+ for succ in bbdata. terminator ( ) . successors ( ) {
671+ if dominators. dominates ( succ, bb) {
672+ loop_headers. insert ( bb) ;
673+ }
674+ }
675+ }
676+ loop_headers
677+ }
0 commit comments