5151//!
5252//! [libfirm]: <https://pp.ipd.kit.edu/uploads/publikationen/priesner17masterarbeit.pdf>
5353
54- use std:: cell:: OnceCell ;
55-
5654use itertools:: Itertools as _;
5755use rustc_const_eval:: const_eval:: DummyMachine ;
5856use rustc_const_eval:: interpret:: { ImmTy , Immediate , InterpCx , OpTy , Projectable } ;
@@ -100,7 +98,6 @@ impl<'tcx> crate::MirPass<'tcx> for JumpThreading {
10098 map : Map :: new ( tcx, body, Some ( MAX_PLACES ) ) ,
10199 loop_headers : loop_headers ( body) ,
102100 entry_states : IndexVec :: from_elem ( ConditionSet :: default ( ) , & body. basic_blocks ) ,
103- costs : IndexVec :: from_elem ( OnceCell :: new ( ) , & body. basic_blocks ) ,
104101 } ;
105102
106103 for ( bb, bbdata) in traversal:: postorder ( body) {
@@ -136,6 +133,7 @@ impl<'tcx> crate::MirPass<'tcx> for JumpThreading {
136133
137134 let mut entry_states = finder. entry_states ;
138135 simplify_conditions ( body, & mut entry_states) ;
136+ remove_costly_conditions ( tcx, typing_env, body, & mut entry_states) ;
139137
140138 if let Some ( opportunities) = OpportunitySet :: new ( body, entry_states) {
141139 opportunities. apply ( ) ;
@@ -159,8 +157,6 @@ struct TOFinder<'a, 'tcx> {
159157 // Invariant: for each `bb`, each condition in `entry_states[bb]` has a `chain` that
160158 // starts with `bb`.
161159 entry_states : IndexVec < BasicBlock , ConditionSet > ,
162- /// Pre-computed cost of duplicating each block.
163- costs : IndexVec < BasicBlock , OnceCell < usize > > ,
164160}
165161
166162rustc_index:: newtype_index! {
@@ -219,7 +215,6 @@ struct ConditionSet {
219215 active : Vec < ( ConditionIndex , Condition ) > ,
220216 fulfilled : Vec < ConditionIndex > ,
221217 targets : IndexVec < ConditionIndex , Vec < ConditionTarget > > ,
222- costs : IndexVec < ConditionIndex , u8 > ,
223218}
224219
225220impl ConditionSet {
@@ -230,7 +225,6 @@ impl ConditionSet {
230225 #[ tracing:: instrument( level = "trace" , skip( self ) ) ]
231226 fn push_condition ( & mut self , c : Condition , succ : BasicBlock ) {
232227 let index = self . targets . push ( vec ! [ ConditionTarget :: Goto ( succ) ] ) ;
233- self . costs . push ( 0 ) ;
234228 self . active . push ( ( index, c) ) ;
235229 }
236230
@@ -290,21 +284,18 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> {
290284 active : Vec :: with_capacity ( state_len) ,
291285 targets : IndexVec :: with_capacity ( state_len) ,
292286 fulfilled : Vec :: new ( ) ,
293- costs : IndexVec :: with_capacity ( state_len) ,
294287 } ;
295288
296289 // Use an index-set to deduplicate conditions coming from different successor blocks.
297290 let mut known_conditions =
298291 FxIndexSet :: with_capacity_and_hasher ( state_len, Default :: default ( ) ) ;
299- let mut insert = |condition, succ_block, succ_cond, cost | {
292+ let mut insert = |condition, succ_block, succ_cond| {
300293 let ( index, new) = known_conditions. insert_full ( condition) ;
301294 let index = ConditionIndex :: from_usize ( index) ;
302295 if new {
303296 state. active . push ( ( index, condition) ) ;
304297 let _index = state. targets . push ( Vec :: new ( ) ) ;
305298 debug_assert_eq ! ( _index, index) ;
306- let _index = state. costs . push ( u8:: MAX ) ;
307- debug_assert_eq ! ( _index, index) ;
308299 }
309300 let target = ConditionTarget :: Chain ( succ_block, succ_cond) ;
310301 debug_assert ! (
@@ -313,7 +304,6 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> {
313304 & state. targets[ index] ,
314305 ) ;
315306 state. targets [ index] . push ( target) ;
316- state. costs [ index] = std:: cmp:: min ( state. costs [ index] , cost) ;
317307 } ;
318308
319309 // A given block may have several times the same successor.
@@ -328,35 +318,19 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> {
328318 continue ;
329319 }
330320
331- let succ_cost = self . cost ( succ) ;
332321 for & ( succ_index, cond) in self . entry_states [ succ] . active . iter ( ) {
333- let cost = self . entry_states [ succ] . costs [ succ_index] ;
334- if let Ok ( cost) = ( ( cost as usize ) + succ_cost) . try_into ( )
335- && cost < MAX_COST
336- {
337- insert ( cond, succ, succ_index, cost) ;
338- }
322+ insert ( cond, succ, succ_index) ;
339323 }
340324 }
341325
342326 let num_conditions = known_conditions. len ( ) ;
343327 debug_assert_eq ! ( num_conditions, state. active. len( ) ) ;
344328 debug_assert_eq ! ( num_conditions, state. targets. len( ) ) ;
345- debug_assert_eq ! ( num_conditions, state. costs. len( ) ) ;
346329 state. fulfilled . reserve ( num_conditions) ;
347330
348331 state
349332 }
350333
351- fn cost ( & self , bb : BasicBlock ) -> usize {
352- * self . costs [ bb] . get_or_init ( || {
353- let bbdata = & self . body [ bb] ;
354- let mut cost = CostChecker :: new ( self . tcx , self . typing_env , None , self . body ) ;
355- cost. visit_basic_block_data ( bb, bbdata) ;
356- cost. cost ( )
357- } )
358- }
359-
360334 /// Remove all conditions in the state that alias given place.
361335 fn flood_state (
362336 & self ,
@@ -753,8 +727,6 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> {
753727 // Fulfilling `index` may thread conditions that we do not want,
754728 // so create a brand new index to immediately mark fulfilled.
755729 let index = state. targets . push ( new_edges) ;
756- let _index = state. costs . push ( 0 ) ;
757- debug_assert_eq ! ( _index, index) ;
758730 state. fulfilled . push ( index) ;
759731 }
760732 }
@@ -867,6 +839,82 @@ fn simplify_conditions(body: &Body<'_>, entry_states: &mut IndexVec<BasicBlock,
867839 }
868840}
869841
842+ #[ instrument( level = "debug" , skip( tcx, typing_env, body, entry_states) ) ]
843+ fn remove_costly_conditions < ' tcx > (
844+ tcx : TyCtxt < ' tcx > ,
845+ typing_env : ty:: TypingEnv < ' tcx > ,
846+ body : & Body < ' tcx > ,
847+ entry_states : & mut IndexVec < BasicBlock , ConditionSet > ,
848+ ) {
849+ let basic_blocks = & body. basic_blocks ;
850+
851+ let mut costs = IndexVec :: from_elem ( None , basic_blocks) ;
852+ let mut cost = |bb : BasicBlock | -> u8 {
853+ let c = * costs[ bb] . get_or_insert_with ( || {
854+ let bbdata = & basic_blocks[ bb] ;
855+ let mut cost = CostChecker :: new ( tcx, typing_env, None , body) ;
856+ cost. visit_basic_block_data ( bb, bbdata) ;
857+ cost. cost ( ) . try_into ( ) . unwrap_or ( MAX_COST )
858+ } ) ;
859+ trace ! ( "cost[{bb:?}] = {c}" ) ;
860+ c
861+ } ;
862+
863+ // Initialize costs with `MAX_COST`: if we have a cycle, the cyclic `bb` has infinite costs.
864+ let mut condition_cost = IndexVec :: from_fn_n (
865+ |bb : BasicBlock | IndexVec :: from_elem_n ( MAX_COST , entry_states[ bb] . targets . len ( ) ) ,
866+ entry_states. len ( ) ,
867+ ) ;
868+
869+ let reverse_postorder = basic_blocks. reverse_postorder ( ) ;
870+
871+ for & bb in reverse_postorder. iter ( ) . rev ( ) {
872+ let state = & entry_states[ bb] ;
873+ trace ! ( ?bb, ?state) ;
874+
875+ let mut current_costs = IndexVec :: from_elem ( 0u8 , & state. targets ) ;
876+
877+ for ( condition, targets) in state. targets . iter_enumerated ( ) {
878+ for & target in targets {
879+ match target {
880+ // A `Goto` has cost 0.
881+ ConditionTarget :: Goto ( _) => { }
882+ // Chaining into an already-fulfilled condition is nop.
883+ ConditionTarget :: Chain ( target, target_condition)
884+ if entry_states[ target] . fulfilled . contains ( & target_condition) => { }
885+ // When chaining, use `cost[target][target_condition] + cost(target)`.
886+ ConditionTarget :: Chain ( target, target_condition) => {
887+ // Cost associated with duplicating `target`.
888+ let duplication_cost = cost ( target) ;
889+ // Cost associated with the rest of the chain.
890+ let target_cost =
891+ * condition_cost[ target] . get ( target_condition) . unwrap_or ( & MAX_COST ) ;
892+ let cost = current_costs[ condition]
893+ . saturating_add ( duplication_cost)
894+ . saturating_add ( target_cost) ;
895+ trace ! ( ?condition, ?target, ?duplication_cost, ?target_cost) ;
896+ current_costs[ condition] = cost;
897+ }
898+ }
899+ }
900+ }
901+
902+ trace ! ( "condition_cost[{bb:?}] = {:?}" , current_costs) ;
903+ condition_cost[ bb] = current_costs;
904+ }
905+
906+ trace ! ( ?condition_cost) ;
907+
908+ for & bb in reverse_postorder {
909+ for ( index, targets) in entry_states[ bb] . targets . iter_enumerated_mut ( ) {
910+ if condition_cost[ bb] [ index] >= MAX_COST {
911+ trace ! ( ?bb, ?index, ?targets, c = ?condition_cost[ bb] [ index] , "remove" ) ;
912+ targets. clear ( )
913+ }
914+ }
915+ }
916+ }
917+
870918struct OpportunitySet < ' a , ' tcx > {
871919 basic_blocks : & ' a mut IndexVec < BasicBlock , BasicBlockData < ' tcx > > ,
872920 entry_states : IndexVec < BasicBlock , ConditionSet > ,
@@ -889,7 +937,6 @@ impl<'a, 'tcx> OpportunitySet<'a, 'tcx> {
889937 // Free some memory, because we will need to clone condition sets.
890938 for state in entry_states. iter_mut ( ) {
891939 state. active = Default :: default ( ) ;
892- state. costs = Default :: default ( ) ;
893940 }
894941 let duplicates = Default :: default ( ) ;
895942 let basic_blocks = body. basic_blocks . as_mut ( ) ;
0 commit comments