3636//! cost by `MAX_COST`.
3737
3838use rustc_arena:: DroplessArena ;
39+ use rustc_const_eval:: interpret:: { ImmTy , Immediate , InterpCx , OpTy , Projectable } ;
3940use rustc_data_structures:: fx:: FxHashSet ;
4041use rustc_index:: bit_set:: BitSet ;
4142use rustc_index:: IndexVec ;
43+ use rustc_middle:: mir:: interpret:: Scalar ;
4244use rustc_middle:: mir:: visit:: Visitor ;
4345use rustc_middle:: mir:: * ;
44- use rustc_middle:: ty:: { self , ScalarInt , Ty , TyCtxt } ;
46+ use rustc_middle:: ty:: layout:: LayoutOf ;
47+ use rustc_middle:: ty:: { self , ScalarInt , TyCtxt } ;
4548use rustc_mir_dataflow:: value_analysis:: { Map , PlaceIndex , State , TrackElem } ;
49+ use rustc_span:: DUMMY_SP ;
4650use rustc_target:: abi:: { TagEncoding , Variants } ;
4751
4852use crate :: cost_checker:: CostChecker ;
53+ use crate :: dataflow_const_prop:: DummyMachine ;
4954
5055pub struct JumpThreading ;
5156
@@ -71,6 +76,7 @@ impl<'tcx> MirPass<'tcx> for JumpThreading {
7176 let mut finder = TOFinder {
7277 tcx,
7378 param_env,
79+ ecx : InterpCx :: new ( tcx, DUMMY_SP , param_env, DummyMachine ) ,
7480 body,
7581 arena : & arena,
7682 map : & map,
@@ -88,7 +94,7 @@ impl<'tcx> MirPass<'tcx> for JumpThreading {
8894 debug ! ( ?discr, ?bb) ;
8995
9096 let discr_ty = discr. ty ( body, tcx) . ty ;
91- let Ok ( discr_layout) = tcx . layout_of ( param_env . and ( discr_ty) ) else { continue } ;
97+ let Ok ( discr_layout) = finder . ecx . layout_of ( discr_ty) else { continue } ;
9298
9399 let Some ( discr) = finder. map . find ( discr. as_ref ( ) ) else { continue } ;
94100 debug ! ( ?discr) ;
@@ -142,6 +148,7 @@ struct ThreadingOpportunity {
142148struct TOFinder < ' tcx , ' a > {
143149 tcx : TyCtxt < ' tcx > ,
144150 param_env : ty:: ParamEnv < ' tcx > ,
151+ ecx : InterpCx < ' tcx , ' tcx , DummyMachine > ,
145152 body : & ' a Body < ' tcx > ,
146153 map : & ' a Map ,
147154 loop_headers : & ' a BitSet < BasicBlock > ,
@@ -329,25 +336,82 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
329336 }
330337
331338 #[ instrument( level = "trace" , skip( self ) ) ]
332- fn process_operand (
339+ fn process_immediate (
333340 & mut self ,
334341 bb : BasicBlock ,
335342 lhs : PlaceIndex ,
336- rhs : & Operand < ' tcx > ,
343+ rhs : ImmTy < ' tcx > ,
337344 state : & mut State < ConditionSet < ' a > > ,
338345 ) -> Option < !> {
339346 let register_opportunity = |c : Condition | {
340347 debug ! ( ?bb, ?c. target, "register" ) ;
341348 self . opportunities . push ( ThreadingOpportunity { chain : vec ! [ bb] , target : c. target } )
342349 } ;
343350
351+ let conditions = state. try_get_idx ( lhs, self . map ) ?;
352+ if let Immediate :: Scalar ( Scalar :: Int ( int) ) = * rhs {
353+ conditions. iter_matches ( int) . for_each ( register_opportunity) ;
354+ }
355+
356+ None
357+ }
358+
359+ /// If we expect `lhs ?= A`, we have an opportunity if we assume `constant == A`.
360+ #[ instrument( level = "trace" , skip( self ) ) ]
361+ fn process_constant (
362+ & mut self ,
363+ bb : BasicBlock ,
364+ lhs : PlaceIndex ,
365+ constant : OpTy < ' tcx > ,
366+ state : & mut State < ConditionSet < ' a > > ,
367+ ) {
368+ self . map . for_each_projection_value (
369+ lhs,
370+ constant,
371+ & mut |elem, op| match elem {
372+ TrackElem :: Field ( idx) => self . ecx . project_field ( op, idx. as_usize ( ) ) . ok ( ) ,
373+ TrackElem :: Variant ( idx) => self . ecx . project_downcast ( op, idx) . ok ( ) ,
374+ TrackElem :: Discriminant => {
375+ let variant = self . ecx . read_discriminant ( op) . ok ( ) ?;
376+ let discr_value =
377+ self . ecx . discriminant_for_variant ( op. layout . ty , variant) . ok ( ) ?;
378+ Some ( discr_value. into ( ) )
379+ }
380+ TrackElem :: DerefLen => {
381+ let op: OpTy < ' _ > = self . ecx . deref_pointer ( op) . ok ( ) ?. into ( ) ;
382+ let len_usize = op. len ( & self . ecx ) . ok ( ) ?;
383+ let layout = self . ecx . layout_of ( self . tcx . types . usize ) . unwrap ( ) ;
384+ Some ( ImmTy :: from_uint ( len_usize, layout) . into ( ) )
385+ }
386+ } ,
387+ & mut |place, op| {
388+ if let Some ( conditions) = state. try_get_idx ( place, self . map )
389+ && let Ok ( imm) = self . ecx . read_immediate_raw ( op)
390+ && let Some ( imm) = imm. right ( )
391+ && let Immediate :: Scalar ( Scalar :: Int ( int) ) = * imm
392+ {
393+ conditions. iter_matches ( int) . for_each ( |c : Condition | {
394+ self . opportunities
395+ . push ( ThreadingOpportunity { chain : vec ! [ bb] , target : c. target } )
396+ } )
397+ }
398+ } ,
399+ ) ;
400+ }
401+
402+ #[ instrument( level = "trace" , skip( self ) ) ]
403+ fn process_operand (
404+ & mut self ,
405+ bb : BasicBlock ,
406+ lhs : PlaceIndex ,
407+ rhs : & Operand < ' tcx > ,
408+ state : & mut State < ConditionSet < ' a > > ,
409+ ) -> Option < !> {
344410 match rhs {
345411 // If we expect `lhs ?= A`, we have an opportunity if we assume `constant == A`.
346412 Operand :: Constant ( constant) => {
347- let conditions = state. try_get_idx ( lhs, self . map ) ?;
348- let constant =
349- constant. const_ . normalize ( self . tcx , self . param_env ) . try_to_scalar_int ( ) ?;
350- conditions. iter_matches ( constant) . for_each ( register_opportunity) ;
413+ let constant = self . ecx . eval_mir_constant ( & constant. const_ , None , None ) . ok ( ) ?;
414+ self . process_constant ( bb, lhs, constant, state) ;
351415 }
352416 // Transfer the conditions on the copied rhs.
353417 Operand :: Move ( rhs) | Operand :: Copy ( rhs) => {
@@ -359,6 +423,84 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
359423 None
360424 }
361425
426+ #[ instrument( level = "trace" , skip( self ) ) ]
427+ fn process_assign (
428+ & mut self ,
429+ bb : BasicBlock ,
430+ lhs_place : & Place < ' tcx > ,
431+ rhs : & Rvalue < ' tcx > ,
432+ state : & mut State < ConditionSet < ' a > > ,
433+ ) -> Option < !> {
434+ let lhs = self . map . find ( lhs_place. as_ref ( ) ) ?;
435+ match rhs {
436+ Rvalue :: Use ( operand) => self . process_operand ( bb, lhs, operand, state) ?,
437+ // Transfer the conditions on the copy rhs.
438+ Rvalue :: CopyForDeref ( rhs) => {
439+ self . process_operand ( bb, lhs, & Operand :: Copy ( * rhs) , state) ?
440+ }
441+ Rvalue :: Discriminant ( rhs) => {
442+ let rhs = self . map . find_discr ( rhs. as_ref ( ) ) ?;
443+ state. insert_place_idx ( rhs, lhs, self . map ) ;
444+ }
445+ // If we expect `lhs ?= A`, we have an opportunity if we assume `constant == A`.
446+ Rvalue :: Aggregate ( box ref kind, ref operands) => {
447+ let agg_ty = lhs_place. ty ( self . body , self . tcx ) . ty ;
448+ let lhs = match kind {
449+ // Do not support unions.
450+ AggregateKind :: Adt ( .., Some ( _) ) => return None ,
451+ AggregateKind :: Adt ( _, variant_index, ..) if agg_ty. is_enum ( ) => {
452+ if let Some ( discr_target) = self . map . apply ( lhs, TrackElem :: Discriminant )
453+ && let Ok ( discr_value) =
454+ self . ecx . discriminant_for_variant ( agg_ty, * variant_index)
455+ {
456+ self . process_immediate ( bb, discr_target, discr_value, state) ;
457+ }
458+ self . map . apply ( lhs, TrackElem :: Variant ( * variant_index) ) ?
459+ }
460+ _ => lhs,
461+ } ;
462+ for ( field_index, operand) in operands. iter_enumerated ( ) {
463+ if let Some ( field) = self . map . apply ( lhs, TrackElem :: Field ( field_index) ) {
464+ self . process_operand ( bb, field, operand, state) ;
465+ }
466+ }
467+ }
468+ // Transfer the conditions on the copy rhs, after inversing polarity.
469+ Rvalue :: UnaryOp ( UnOp :: Not , Operand :: Move ( place) | Operand :: Copy ( place) ) => {
470+ let conditions = state. try_get_idx ( lhs, self . map ) ?;
471+ let place = self . map . find ( place. as_ref ( ) ) ?;
472+ let conds = conditions. map ( self . arena , Condition :: inv) ;
473+ state. insert_value_idx ( place, conds, self . map ) ;
474+ }
475+ // We expect `lhs ?= A`. We found `lhs = Eq(rhs, B)`.
476+ // Create a condition on `rhs ?= B`.
477+ Rvalue :: BinaryOp (
478+ op,
479+ box ( Operand :: Move ( place) | Operand :: Copy ( place) , Operand :: Constant ( value) )
480+ | box ( Operand :: Constant ( value) , Operand :: Move ( place) | Operand :: Copy ( place) ) ,
481+ ) => {
482+ let conditions = state. try_get_idx ( lhs, self . map ) ?;
483+ let place = self . map . find ( place. as_ref ( ) ) ?;
484+ let equals = match op {
485+ BinOp :: Eq => ScalarInt :: TRUE ,
486+ BinOp :: Ne => ScalarInt :: FALSE ,
487+ _ => return None ,
488+ } ;
489+ let value = value. const_ . normalize ( self . tcx , self . param_env ) . try_to_scalar_int ( ) ?;
490+ let conds = conditions. map ( self . arena , |c| Condition {
491+ value,
492+ polarity : if c. matches ( equals) { Polarity :: Eq } else { Polarity :: Ne } ,
493+ ..c
494+ } ) ;
495+ state. insert_value_idx ( place, conds, self . map ) ;
496+ }
497+
498+ _ => { }
499+ }
500+
501+ None
502+ }
503+
362504 #[ instrument( level = "trace" , skip( self ) ) ]
363505 fn process_statement (
364506 & mut self ,
@@ -374,18 +516,6 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
374516 // Below, `lhs` is the return value of `mutated_statement`,
375517 // the place to which `conditions` apply.
376518
377- let discriminant_for_variant = |enum_ty : Ty < ' tcx > , variant_index| {
378- let discr = enum_ty. discriminant_for_variant ( self . tcx , variant_index) ?;
379- let discr_layout = self . tcx . layout_of ( self . param_env . and ( discr. ty ) ) . ok ( ) ?;
380- let scalar = ScalarInt :: try_from_uint ( discr. val , discr_layout. size ) ?;
381- Some ( Operand :: const_from_scalar (
382- self . tcx ,
383- discr. ty ,
384- scalar. into ( ) ,
385- rustc_span:: DUMMY_SP ,
386- ) )
387- } ;
388-
389519 match & stmt. kind {
390520 // If we expect `discriminant(place) ?= A`,
391521 // we have an opportunity if `variant_index ?= A`.
@@ -395,7 +525,7 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
395525 // `SetDiscriminant` may be a no-op if the assigned variant is the untagged variant
396526 // of a niche encoding. If we cannot ensure that we write to the discriminant, do
397527 // nothing.
398- let enum_layout = self . tcx . layout_of ( self . param_env . and ( enum_ty) ) . ok ( ) ?;
528+ let enum_layout = self . ecx . layout_of ( enum_ty) . ok ( ) ?;
399529 let writes_discriminant = match enum_layout. variants {
400530 Variants :: Single { index } => {
401531 assert_eq ! ( index, * variant_index) ;
@@ -408,8 +538,8 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
408538 } => * variant_index != untagged_variant,
409539 } ;
410540 if writes_discriminant {
411- let discr = discriminant_for_variant ( enum_ty, * variant_index) ?;
412- self . process_operand ( bb, discr_target, & discr, state) ?;
541+ let discr = self . ecx . discriminant_for_variant ( enum_ty, * variant_index) . ok ( ) ?;
542+ self . process_immediate ( bb, discr_target, discr, state) ?;
413543 }
414544 }
415545 // If we expect `lhs ?= true`, we have an opportunity if we assume `lhs == true`.
@@ -420,89 +550,7 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
420550 conditions. iter_matches ( ScalarInt :: TRUE ) . for_each ( register_opportunity) ;
421551 }
422552 StatementKind :: Assign ( box ( lhs_place, rhs) ) => {
423- if let Some ( lhs) = self . map . find ( lhs_place. as_ref ( ) ) {
424- match rhs {
425- Rvalue :: Use ( operand) => self . process_operand ( bb, lhs, operand, state) ?,
426- // Transfer the conditions on the copy rhs.
427- Rvalue :: CopyForDeref ( rhs) => {
428- self . process_operand ( bb, lhs, & Operand :: Copy ( * rhs) , state) ?
429- }
430- Rvalue :: Discriminant ( rhs) => {
431- let rhs = self . map . find_discr ( rhs. as_ref ( ) ) ?;
432- state. insert_place_idx ( rhs, lhs, self . map ) ;
433- }
434- // If we expect `lhs ?= A`, we have an opportunity if we assume `constant == A`.
435- Rvalue :: Aggregate ( box ref kind, ref operands) => {
436- let agg_ty = lhs_place. ty ( self . body , self . tcx ) . ty ;
437- let lhs = match kind {
438- // Do not support unions.
439- AggregateKind :: Adt ( .., Some ( _) ) => return None ,
440- AggregateKind :: Adt ( _, variant_index, ..) if agg_ty. is_enum ( ) => {
441- if let Some ( discr_target) =
442- self . map . apply ( lhs, TrackElem :: Discriminant )
443- && let Some ( discr_value) =
444- discriminant_for_variant ( agg_ty, * variant_index)
445- {
446- self . process_operand ( bb, discr_target, & discr_value, state) ;
447- }
448- self . map . apply ( lhs, TrackElem :: Variant ( * variant_index) ) ?
449- }
450- _ => lhs,
451- } ;
452- for ( field_index, operand) in operands. iter_enumerated ( ) {
453- if let Some ( field) =
454- self . map . apply ( lhs, TrackElem :: Field ( field_index) )
455- {
456- self . process_operand ( bb, field, operand, state) ;
457- }
458- }
459- }
460- // Transfer the conditions on the copy rhs, after inversing polarity.
461- Rvalue :: UnaryOp ( UnOp :: Not , Operand :: Move ( place) | Operand :: Copy ( place) ) => {
462- let conditions = state. try_get_idx ( lhs, self . map ) ?;
463- let place = self . map . find ( place. as_ref ( ) ) ?;
464- let conds = conditions. map ( self . arena , Condition :: inv) ;
465- state. insert_value_idx ( place, conds, self . map ) ;
466- }
467- // We expect `lhs ?= A`. We found `lhs = Eq(rhs, B)`.
468- // Create a condition on `rhs ?= B`.
469- Rvalue :: BinaryOp (
470- op,
471- box (
472- Operand :: Move ( place) | Operand :: Copy ( place) ,
473- Operand :: Constant ( value) ,
474- )
475- | box (
476- Operand :: Constant ( value) ,
477- Operand :: Move ( place) | Operand :: Copy ( place) ,
478- ) ,
479- ) => {
480- let conditions = state. try_get_idx ( lhs, self . map ) ?;
481- let place = self . map . find ( place. as_ref ( ) ) ?;
482- let equals = match op {
483- BinOp :: Eq => ScalarInt :: TRUE ,
484- BinOp :: Ne => ScalarInt :: FALSE ,
485- _ => return None ,
486- } ;
487- let value = value
488- . const_
489- . normalize ( self . tcx , self . param_env )
490- . try_to_scalar_int ( ) ?;
491- let conds = conditions. map ( self . arena , |c| Condition {
492- value,
493- polarity : if c. matches ( equals) {
494- Polarity :: Eq
495- } else {
496- Polarity :: Ne
497- } ,
498- ..c
499- } ) ;
500- state. insert_value_idx ( place, conds, self . map ) ;
501- }
502-
503- _ => { }
504- }
505- }
553+ self . process_assign ( bb, lhs_place, rhs, state) ?;
506554 }
507555 _ => { }
508556 }
@@ -577,7 +625,7 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
577625
578626 let discr = discr. place ( ) ?;
579627 let discr_ty = discr. ty ( self . body , self . tcx ) . ty ;
580- let discr_layout = self . tcx . layout_of ( self . param_env . and ( discr_ty) ) . ok ( ) ?;
628+ let discr_layout = self . ecx . layout_of ( discr_ty) . ok ( ) ?;
581629 let conditions = state. try_get ( discr. as_ref ( ) , self . map ) ?;
582630
583631 if let Some ( ( value, _) ) = targets. iter ( ) . find ( |& ( _, target) | target == target_bb) {
0 commit comments