51
51
//!
52
52
//! [libfirm]: <https://pp.ipd.kit.edu/uploads/publikationen/priesner17masterarbeit.pdf>
53
53
54
- use std:: cell:: OnceCell ;
55
-
56
54
use itertools:: Itertools as _;
57
55
use rustc_const_eval:: const_eval:: DummyMachine ;
58
56
use rustc_const_eval:: interpret:: { ImmTy , Immediate , InterpCx , OpTy , Projectable } ;
@@ -100,7 +98,6 @@ impl<'tcx> crate::MirPass<'tcx> for JumpThreading {
100
98
map : Map :: new ( tcx, body, Some ( MAX_PLACES ) ) ,
101
99
maybe_loop_headers : maybe_loop_headers ( body) ,
102
100
entry_states : IndexVec :: from_elem ( ConditionSet :: default ( ) , & body. basic_blocks ) ,
103
- costs : IndexVec :: from_elem ( OnceCell :: new ( ) , & body. basic_blocks ) ,
104
101
} ;
105
102
106
103
for ( bb, bbdata) in traversal:: postorder ( body) {
@@ -136,6 +133,7 @@ impl<'tcx> crate::MirPass<'tcx> for JumpThreading {
136
133
137
134
let mut entry_states = finder. entry_states ;
138
135
simplify_conditions ( body, & mut entry_states) ;
136
+ remove_costly_conditions ( tcx, typing_env, body, & mut entry_states) ;
139
137
140
138
if let Some ( opportunities) = OpportunitySet :: new ( body, entry_states) {
141
139
opportunities. apply ( ) ;
@@ -159,8 +157,6 @@ struct TOFinder<'a, 'tcx> {
159
157
// Invariant: for each `bb`, each condition in `entry_states[bb]` has a `chain` that
160
158
// starts with `bb`.
161
159
entry_states : IndexVec < BasicBlock , ConditionSet > ,
162
- /// Pre-computed cost of duplicating each block.
163
- costs : IndexVec < BasicBlock , OnceCell < usize > > ,
164
160
}
165
161
166
162
rustc_index:: newtype_index! {
@@ -222,7 +218,6 @@ struct ConditionSet {
222
218
active : Vec < ( ConditionIndex , Condition ) > ,
223
219
fulfilled : Vec < ConditionIndex > ,
224
220
targets : IndexVec < ConditionIndex , Vec < EdgeEffect > > ,
225
- costs : IndexVec < ConditionIndex , u8 > ,
226
221
}
227
222
228
223
impl ConditionSet {
@@ -233,7 +228,6 @@ impl ConditionSet {
233
228
#[ tracing:: instrument( level = "trace" , skip( self ) ) ]
234
229
fn push_condition ( & mut self , c : Condition , target : BasicBlock ) {
235
230
let index = self . targets . push ( vec ! [ EdgeEffect :: Goto { target } ] ) ;
236
- self . costs . push ( 0 ) ;
237
231
self . active . push ( ( index, c) ) ;
238
232
}
239
233
@@ -293,21 +287,18 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> {
293
287
active : Vec :: with_capacity ( state_len) ,
294
288
targets : IndexVec :: with_capacity ( state_len) ,
295
289
fulfilled : Vec :: new ( ) ,
296
- costs : IndexVec :: with_capacity ( state_len) ,
297
290
} ;
298
291
299
292
// Use an index-set to deduplicate conditions coming from different successor blocks.
300
293
let mut known_conditions =
301
294
FxIndexSet :: with_capacity_and_hasher ( state_len, Default :: default ( ) ) ;
302
- let mut insert = |condition, succ_block, succ_condition, cost | {
295
+ let mut insert = |condition, succ_block, succ_condition| {
303
296
let ( index, new) = known_conditions. insert_full ( condition) ;
304
297
let index = ConditionIndex :: from_usize ( index) ;
305
298
if new {
306
299
state. active . push ( ( index, condition) ) ;
307
300
let _index = state. targets . push ( Vec :: new ( ) ) ;
308
301
debug_assert_eq ! ( _index, index) ;
309
- let _index = state. costs . push ( u8:: MAX ) ;
310
- debug_assert_eq ! ( _index, index) ;
311
302
}
312
303
let target = EdgeEffect :: Chain { succ_block, succ_condition } ;
313
304
debug_assert ! (
@@ -316,7 +307,6 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> {
316
307
& state. targets[ index] ,
317
308
) ;
318
309
state. targets [ index] . push ( target) ;
319
- state. costs [ index] = std:: cmp:: min ( state. costs [ index] , cost) ;
320
310
} ;
321
311
322
312
// A given block may have several times the same successor.
@@ -331,35 +321,19 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> {
331
321
continue ;
332
322
}
333
323
334
- let succ_cost = self . cost ( succ) ;
335
324
for & ( succ_index, cond) in self . entry_states [ succ] . active . iter ( ) {
336
- let cost = self . entry_states [ succ] . costs [ succ_index] ;
337
- if let Ok ( cost) = ( ( cost as usize ) + succ_cost) . try_into ( )
338
- && cost < MAX_COST
339
- {
340
- insert ( cond, succ, succ_index, cost) ;
341
- }
325
+ insert ( cond, succ, succ_index) ;
342
326
}
343
327
}
344
328
345
329
let num_conditions = known_conditions. len ( ) ;
346
330
debug_assert_eq ! ( num_conditions, state. active. len( ) ) ;
347
331
debug_assert_eq ! ( num_conditions, state. targets. len( ) ) ;
348
- debug_assert_eq ! ( num_conditions, state. costs. len( ) ) ;
349
332
state. fulfilled . reserve ( num_conditions) ;
350
333
351
334
state
352
335
}
353
336
354
- fn cost ( & self , bb : BasicBlock ) -> usize {
355
- * self . costs [ bb] . get_or_init ( || {
356
- let bbdata = & self . body [ bb] ;
357
- let mut cost = CostChecker :: new ( self . tcx , self . typing_env , None , self . body ) ;
358
- cost. visit_basic_block_data ( bb, bbdata) ;
359
- cost. cost ( )
360
- } )
361
- }
362
-
363
337
/// Remove all conditions in the state that alias given place.
364
338
fn flood_state (
365
339
& self ,
@@ -756,8 +730,6 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> {
756
730
// Fulfilling `index` may thread conditions that we do not want,
757
731
// so create a brand new index to immediately mark fulfilled.
758
732
let index = state. targets . push ( new_edges) ;
759
- let _index = state. costs . push ( 0 ) ;
760
- debug_assert_eq ! ( _index, index) ;
761
733
state. fulfilled . push ( index) ;
762
734
}
763
735
}
@@ -870,6 +842,82 @@ fn simplify_conditions(body: &Body<'_>, entry_states: &mut IndexVec<BasicBlock,
870
842
}
871
843
}
872
844
845
+ #[ instrument( level = "debug" , skip( tcx, typing_env, body, entry_states) ) ]
846
+ fn remove_costly_conditions < ' tcx > (
847
+ tcx : TyCtxt < ' tcx > ,
848
+ typing_env : ty:: TypingEnv < ' tcx > ,
849
+ body : & Body < ' tcx > ,
850
+ entry_states : & mut IndexVec < BasicBlock , ConditionSet > ,
851
+ ) {
852
+ let basic_blocks = & body. basic_blocks ;
853
+
854
+ let mut costs = IndexVec :: from_elem ( None , basic_blocks) ;
855
+ let mut cost = |bb : BasicBlock | -> u8 {
856
+ let c = * costs[ bb] . get_or_insert_with ( || {
857
+ let bbdata = & basic_blocks[ bb] ;
858
+ let mut cost = CostChecker :: new ( tcx, typing_env, None , body) ;
859
+ cost. visit_basic_block_data ( bb, bbdata) ;
860
+ cost. cost ( ) . try_into ( ) . unwrap_or ( MAX_COST )
861
+ } ) ;
862
+ trace ! ( "cost[{bb:?}] = {c}" ) ;
863
+ c
864
+ } ;
865
+
866
+ // Initialize costs with `MAX_COST`: if we have a cycle, the cyclic `bb` has infinite costs.
867
+ let mut condition_cost = IndexVec :: from_fn_n (
868
+ |bb : BasicBlock | IndexVec :: from_elem_n ( MAX_COST , entry_states[ bb] . targets . len ( ) ) ,
869
+ entry_states. len ( ) ,
870
+ ) ;
871
+
872
+ let reverse_postorder = basic_blocks. reverse_postorder ( ) ;
873
+
874
+ for & bb in reverse_postorder. iter ( ) . rev ( ) {
875
+ let state = & entry_states[ bb] ;
876
+ trace ! ( ?bb, ?state) ;
877
+
878
+ let mut current_costs = IndexVec :: from_elem ( 0u8 , & state. targets ) ;
879
+
880
+ for ( condition, targets) in state. targets . iter_enumerated ( ) {
881
+ for & target in targets {
882
+ match target {
883
+ // A `Goto` has cost 0.
884
+ EdgeEffect :: Goto { .. } => { }
885
+ // Chaining into an already-fulfilled condition is nop.
886
+ EdgeEffect :: Chain { succ_block, succ_condition }
887
+ if entry_states[ succ_block] . fulfilled . contains ( & succ_condition) => { }
888
+ // When chaining, use `cost[succ_block][succ_condition] + cost(succ_block)`.
889
+ EdgeEffect :: Chain { succ_block, succ_condition } => {
890
+ // Cost associated with duplicating `succ_block`.
891
+ let duplication_cost = cost ( succ_block) ;
892
+ // Cost associated with the rest of the chain.
893
+ let target_cost =
894
+ * condition_cost[ succ_block] . get ( succ_condition) . unwrap_or ( & MAX_COST ) ;
895
+ let cost = current_costs[ condition]
896
+ . saturating_add ( duplication_cost)
897
+ . saturating_add ( target_cost) ;
898
+ trace ! ( ?condition, ?succ_block, ?duplication_cost, ?target_cost) ;
899
+ current_costs[ condition] = cost;
900
+ }
901
+ }
902
+ }
903
+ }
904
+
905
+ trace ! ( "condition_cost[{bb:?}] = {:?}" , current_costs) ;
906
+ condition_cost[ bb] = current_costs;
907
+ }
908
+
909
+ trace ! ( ?condition_cost) ;
910
+
911
+ for & bb in reverse_postorder {
912
+ for ( index, targets) in entry_states[ bb] . targets . iter_enumerated_mut ( ) {
913
+ if condition_cost[ bb] [ index] >= MAX_COST {
914
+ trace ! ( ?bb, ?index, ?targets, c = ?condition_cost[ bb] [ index] , "remove" ) ;
915
+ targets. clear ( )
916
+ }
917
+ }
918
+ }
919
+ }
920
+
873
921
struct OpportunitySet < ' a , ' tcx > {
874
922
basic_blocks : & ' a mut IndexVec < BasicBlock , BasicBlockData < ' tcx > > ,
875
923
entry_states : IndexVec < BasicBlock , ConditionSet > ,
@@ -892,7 +940,6 @@ impl<'a, 'tcx> OpportunitySet<'a, 'tcx> {
892
940
// Free some memory, because we will need to clone condition sets.
893
941
for state in entry_states. iter_mut ( ) {
894
942
state. active = Default :: default ( ) ;
895
- state. costs = Default :: default ( ) ;
896
943
}
897
944
let duplicates = Default :: default ( ) ;
898
945
let basic_blocks = body. basic_blocks . as_mut ( ) ;
0 commit comments