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
loop_headers : 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! {
@@ -219,7 +215,6 @@ struct ConditionSet {
219
215
active : Vec < ( ConditionIndex , Condition ) > ,
220
216
fulfilled : Vec < ConditionIndex > ,
221
217
targets : IndexVec < ConditionIndex , Vec < ConditionTarget > > ,
222
- costs : IndexVec < ConditionIndex , u8 > ,
223
218
}
224
219
225
220
impl ConditionSet {
@@ -230,7 +225,6 @@ impl ConditionSet {
230
225
#[ tracing:: instrument( level = "trace" , skip( self ) ) ]
231
226
fn push_condition ( & mut self , c : Condition , succ : BasicBlock ) {
232
227
let index = self . targets . push ( vec ! [ ConditionTarget :: Goto ( succ) ] ) ;
233
- self . costs . push ( 0 ) ;
234
228
self . active . push ( ( index, c) ) ;
235
229
}
236
230
@@ -290,21 +284,18 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> {
290
284
active : Vec :: with_capacity ( state_len) ,
291
285
targets : IndexVec :: with_capacity ( state_len) ,
292
286
fulfilled : Vec :: new ( ) ,
293
- costs : IndexVec :: with_capacity ( state_len) ,
294
287
} ;
295
288
296
289
// Use an index-set to deduplicate conditions coming from different successor blocks.
297
290
let mut known_conditions =
298
291
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| {
300
293
let ( index, new) = known_conditions. insert_full ( condition) ;
301
294
let index = ConditionIndex :: from_usize ( index) ;
302
295
if new {
303
296
state. active . push ( ( index, condition) ) ;
304
297
let _index = state. targets . push ( Vec :: new ( ) ) ;
305
298
debug_assert_eq ! ( _index, index) ;
306
- let _index = state. costs . push ( u8:: MAX ) ;
307
- debug_assert_eq ! ( _index, index) ;
308
299
}
309
300
let target = ConditionTarget :: Chain ( succ_block, succ_cond) ;
310
301
debug_assert ! (
@@ -313,7 +304,6 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> {
313
304
& state. targets[ index] ,
314
305
) ;
315
306
state. targets [ index] . push ( target) ;
316
- state. costs [ index] = std:: cmp:: min ( state. costs [ index] , cost) ;
317
307
} ;
318
308
319
309
// A given block may have several times the same successor.
@@ -328,35 +318,19 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> {
328
318
continue ;
329
319
}
330
320
331
- let succ_cost = self . cost ( succ) ;
332
321
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) ;
339
323
}
340
324
}
341
325
342
326
let num_conditions = known_conditions. len ( ) ;
343
327
debug_assert_eq ! ( num_conditions, state. active. len( ) ) ;
344
328
debug_assert_eq ! ( num_conditions, state. targets. len( ) ) ;
345
- debug_assert_eq ! ( num_conditions, state. costs. len( ) ) ;
346
329
state. fulfilled . reserve ( num_conditions) ;
347
330
348
331
state
349
332
}
350
333
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
-
360
334
/// Remove all conditions in the state that alias given place.
361
335
fn flood_state (
362
336
& self ,
@@ -753,8 +727,6 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> {
753
727
// Fulfilling `index` may thread conditions that we do not want,
754
728
// so create a brand new index to immediately mark fulfilled.
755
729
let index = state. targets . push ( new_edges) ;
756
- let _index = state. costs . push ( 0 ) ;
757
- debug_assert_eq ! ( _index, index) ;
758
730
state. fulfilled . push ( index) ;
759
731
}
760
732
}
@@ -867,6 +839,82 @@ fn simplify_conditions(body: &Body<'_>, entry_states: &mut IndexVec<BasicBlock,
867
839
}
868
840
}
869
841
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
+
870
918
struct OpportunitySet < ' a , ' tcx > {
871
919
basic_blocks : & ' a mut IndexVec < BasicBlock , BasicBlockData < ' tcx > > ,
872
920
entry_states : IndexVec < BasicBlock , ConditionSet > ,
@@ -889,7 +937,6 @@ impl<'a, 'tcx> OpportunitySet<'a, 'tcx> {
889
937
// Free some memory, because we will need to clone condition sets.
890
938
for state in entry_states. iter_mut ( ) {
891
939
state. active = Default :: default ( ) ;
892
- state. costs = Default :: default ( ) ;
893
940
}
894
941
let duplicates = Default :: default ( ) ;
895
942
let basic_blocks = body. basic_blocks . as_mut ( ) ;
0 commit comments