36
36
//! cost by `MAX_COST`.
37
37
38
38
use rustc_arena:: DroplessArena ;
39
+ use rustc_const_eval:: interpret:: { ImmTy , Immediate , InterpCx , OpTy , Projectable } ;
39
40
use rustc_data_structures:: fx:: FxHashSet ;
40
41
use rustc_index:: bit_set:: BitSet ;
41
42
use rustc_index:: IndexVec ;
43
+ use rustc_middle:: mir:: interpret:: Scalar ;
42
44
use rustc_middle:: mir:: visit:: Visitor ;
43
45
use 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 } ;
45
48
use rustc_mir_dataflow:: value_analysis:: { Map , PlaceIndex , State , TrackElem } ;
49
+ use rustc_span:: DUMMY_SP ;
46
50
use rustc_target:: abi:: { TagEncoding , Variants } ;
47
51
48
52
use crate :: cost_checker:: CostChecker ;
53
+ use crate :: dataflow_const_prop:: DummyMachine ;
49
54
50
55
pub struct JumpThreading ;
51
56
@@ -71,6 +76,7 @@ impl<'tcx> MirPass<'tcx> for JumpThreading {
71
76
let mut finder = TOFinder {
72
77
tcx,
73
78
param_env,
79
+ ecx : InterpCx :: new ( tcx, DUMMY_SP , param_env, DummyMachine ) ,
74
80
body,
75
81
arena : & arena,
76
82
map : & map,
@@ -88,7 +94,7 @@ impl<'tcx> MirPass<'tcx> for JumpThreading {
88
94
debug ! ( ?discr, ?bb) ;
89
95
90
96
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 } ;
92
98
93
99
let Some ( discr) = finder. map . find ( discr. as_ref ( ) ) else { continue } ;
94
100
debug ! ( ?discr) ;
@@ -142,6 +148,7 @@ struct ThreadingOpportunity {
142
148
struct TOFinder < ' tcx , ' a > {
143
149
tcx : TyCtxt < ' tcx > ,
144
150
param_env : ty:: ParamEnv < ' tcx > ,
151
+ ecx : InterpCx < ' tcx , ' tcx , DummyMachine > ,
145
152
body : & ' a Body < ' tcx > ,
146
153
map : & ' a Map ,
147
154
loop_headers : & ' a BitSet < BasicBlock > ,
@@ -329,25 +336,82 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
329
336
}
330
337
331
338
#[ instrument( level = "trace" , skip( self ) ) ]
332
- fn process_operand (
339
+ fn process_immediate (
333
340
& mut self ,
334
341
bb : BasicBlock ,
335
342
lhs : PlaceIndex ,
336
- rhs : & Operand < ' tcx > ,
343
+ rhs : ImmTy < ' tcx > ,
337
344
state : & mut State < ConditionSet < ' a > > ,
338
345
) -> Option < !> {
339
346
let register_opportunity = |c : Condition | {
340
347
debug ! ( ?bb, ?c. target, "register" ) ;
341
348
self . opportunities . push ( ThreadingOpportunity { chain : vec ! [ bb] , target : c. target } )
342
349
} ;
343
350
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 < !> {
344
410
match rhs {
345
411
// If we expect `lhs ?= A`, we have an opportunity if we assume `constant == A`.
346
412
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) ;
351
415
}
352
416
// Transfer the conditions on the copied rhs.
353
417
Operand :: Move ( rhs) | Operand :: Copy ( rhs) => {
@@ -359,6 +423,84 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
359
423
None
360
424
}
361
425
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
+
362
504
#[ instrument( level = "trace" , skip( self ) ) ]
363
505
fn process_statement (
364
506
& mut self ,
@@ -374,18 +516,6 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
374
516
// Below, `lhs` is the return value of `mutated_statement`,
375
517
// the place to which `conditions` apply.
376
518
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
-
389
519
match & stmt. kind {
390
520
// If we expect `discriminant(place) ?= A`,
391
521
// we have an opportunity if `variant_index ?= A`.
@@ -395,7 +525,7 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
395
525
// `SetDiscriminant` may be a no-op if the assigned variant is the untagged variant
396
526
// of a niche encoding. If we cannot ensure that we write to the discriminant, do
397
527
// 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 ( ) ?;
399
529
let writes_discriminant = match enum_layout. variants {
400
530
Variants :: Single { index } => {
401
531
assert_eq ! ( index, * variant_index) ;
@@ -408,8 +538,8 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
408
538
} => * variant_index != untagged_variant,
409
539
} ;
410
540
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) ?;
413
543
}
414
544
}
415
545
// If we expect `lhs ?= true`, we have an opportunity if we assume `lhs == true`.
@@ -420,89 +550,7 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
420
550
conditions. iter_matches ( ScalarInt :: TRUE ) . for_each ( register_opportunity) ;
421
551
}
422
552
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) ?;
506
554
}
507
555
_ => { }
508
556
}
@@ -577,7 +625,7 @@ impl<'tcx, 'a> TOFinder<'tcx, 'a> {
577
625
578
626
let discr = discr. place ( ) ?;
579
627
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 ( ) ?;
581
629
let conditions = state. try_get ( discr. as_ref ( ) , self . map ) ?;
582
630
583
631
if let Some ( ( value, _) ) = targets. iter ( ) . find ( |& ( _, target) | target == target_bb) {
0 commit comments