8686
8787use std:: borrow:: Cow ;
8888use std:: hash:: { Hash , Hasher } ;
89+ use std:: ops:: AddAssign ;
8990
9091use either:: Either ;
9192use hashbrown:: hash_table:: { Entry , HashTable } ;
9293use itertools:: Itertools as _;
93- use rustc_abi:: { self as abi, BackendRepr , FIRST_VARIANT , FieldIdx , Primitive , Size , VariantIdx } ;
94+ use rustc_abi:: {
95+ self as abi, BackendRepr , FIRST_VARIANT , FieldIdx , Primitive , Size , VariantIdx , WrappingRange ,
96+ } ;
9497use rustc_arena:: DroplessArena ;
9598use rustc_const_eval:: const_eval:: DummyMachine ;
9699use rustc_const_eval:: interpret:: {
97100 ImmTy , Immediate , InterpCx , MemPlaceMeta , MemoryKind , OpTy , Projectable , Scalar ,
98101 intern_const_alloc_for_constprop,
99102} ;
100- use rustc_data_structures:: fx:: FxHasher ;
103+ use rustc_data_structures:: fx:: { FxHashMap , FxHasher } ;
101104use rustc_data_structures:: graph:: dominators:: Dominators ;
102105use rustc_hir:: def:: DefKind ;
103106use rustc_index:: bit_set:: DenseBitSet ;
@@ -130,10 +133,25 @@ impl<'tcx> crate::MirPass<'tcx> for GVN {
130133 // Clone dominators because we need them while mutating the body.
131134 let dominators = body. basic_blocks . dominators ( ) . clone ( ) ;
132135 let maybe_loop_headers = loops:: maybe_loop_headers ( body) ;
136+ let predecessors = body. basic_blocks . predecessors ( ) ;
137+ let mut unique_predecessors = DenseBitSet :: new_empty ( body. basic_blocks . len ( ) ) ;
138+ for ( bb, _) in body. basic_blocks . iter_enumerated ( ) {
139+ if predecessors[ bb] . len ( ) == 1 {
140+ unique_predecessors. insert ( bb) ;
141+ }
142+ }
133143
134144 let arena = DroplessArena :: default ( ) ;
135- let mut state =
136- VnState :: new ( tcx, body, typing_env, & ssa, dominators, & body. local_decls , & arena) ;
145+ let mut state = VnState :: new (
146+ tcx,
147+ body,
148+ typing_env,
149+ & ssa,
150+ dominators,
151+ & body. local_decls ,
152+ & arena,
153+ unique_predecessors,
154+ ) ;
137155
138156 for local in body. args_iter ( ) . filter ( |& local| ssa. is_ssa ( local) ) {
139157 let opaque = state. new_opaque ( body. local_decls [ local] . ty ) ;
@@ -380,6 +398,10 @@ struct VnState<'body, 'a, 'tcx> {
380398 dominators : Dominators < BasicBlock > ,
381399 reused_locals : DenseBitSet < Local > ,
382400 arena : & ' a DroplessArena ,
401+ /// Known ranges at each locations.
402+ ranges : IndexVec < VnIndex , Vec < ( Location , WrappingRange ) > > ,
403+ /// Determines if the basic block has a single unique predecessor.
404+ unique_predecessors : DenseBitSet < BasicBlock > ,
383405}
384406
385407impl < ' body , ' a , ' tcx > VnState < ' body , ' a , ' tcx > {
@@ -391,6 +413,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
391413 dominators : Dominators < BasicBlock > ,
392414 local_decls : & ' body LocalDecls < ' tcx > ,
393415 arena : & ' a DroplessArena ,
416+ unique_predecessors : DenseBitSet < BasicBlock > ,
394417 ) -> Self {
395418 // Compute a rough estimate of the number of values in the body from the number of
396419 // statements. This is meant to reduce the number of allocations, but it's all right if
@@ -413,6 +436,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
413436 dominators,
414437 reused_locals : DenseBitSet :: new_empty ( local_decls. len ( ) ) ,
415438 arena,
439+ ranges : IndexVec :: with_capacity ( num_values) ,
440+ unique_predecessors,
416441 }
417442 }
418443
@@ -429,6 +454,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
429454 debug_assert_eq ! ( index, _index) ;
430455 let _index = self . rev_locals . push ( SmallVec :: new ( ) ) ;
431456 debug_assert_eq ! ( index, _index) ;
457+ let _index = self . ranges . push ( Vec :: new ( ) ) ;
458+ debug_assert_eq ! ( index, _index) ;
432459 }
433460 index
434461 }
@@ -442,6 +469,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
442469 debug_assert_eq ! ( index, _index) ;
443470 let _index = self . rev_locals . push ( SmallVec :: new ( ) ) ;
444471 debug_assert_eq ! ( index, _index) ;
472+ let _index = self . ranges . push ( Vec :: new ( ) ) ;
473+ debug_assert_eq ! ( index, _index) ;
445474 index
446475 }
447476
@@ -480,6 +509,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
480509 debug_assert_eq ! ( index, _index) ;
481510 let _index = self . rev_locals . push ( SmallVec :: new ( ) ) ;
482511 debug_assert_eq ! ( index, _index) ;
512+ let _index = self . ranges . push ( Vec :: new ( ) ) ;
513+ debug_assert_eq ! ( index, _index) ;
483514
484515 Some ( index)
485516 }
@@ -504,6 +535,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
504535 debug_assert_eq ! ( index, _index) ;
505536 let _index = self . rev_locals . push ( SmallVec :: new ( ) ) ;
506537 debug_assert_eq ! ( index, _index) ;
538+ let _index = self . ranges . push ( Vec :: new ( ) ) ;
539+ debug_assert_eq ! ( index, _index) ;
507540 }
508541 index
509542 }
@@ -526,6 +559,20 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
526559 self . rev_locals [ value] . push ( local) ;
527560 }
528561
562+ /// Create a new known range at the location.
563+ fn insert_range ( & mut self , value : VnIndex , location : Location , range : WrappingRange ) {
564+ self . ranges [ value] . push ( ( location, range) ) ;
565+ }
566+
567+ /// Get the known range at the location.
568+ fn get_range ( & self , value : VnIndex , location : Location ) -> Option < WrappingRange > {
569+ // FIXME: This should use the intersection of all valid ranges.
570+ let ( _, range) = self . ranges [ value]
571+ . iter ( )
572+ . find ( |( range_loc, _) | range_loc. dominates ( location, & self . dominators ) ) ?;
573+ Some ( * range)
574+ }
575+
529576 fn insert_bool ( & mut self , flag : bool ) -> VnIndex {
530577 // Booleans are deterministic.
531578 let value = Const :: from_bool ( self . tcx , flag) ;
@@ -1010,7 +1057,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
10101057 Operand :: Constant ( ref constant) => Some ( self . insert_constant ( constant. const_ ) ) ,
10111058 Operand :: Copy ( ref mut place) | Operand :: Move ( ref mut place) => {
10121059 let value = self . simplify_place_value ( place, location) ?;
1013- if let Some ( const_) = self . try_as_constant ( value) {
1060+ if let Some ( const_) = self . try_as_constant ( value, location ) {
10141061 * operand = Operand :: Constant ( Box :: new ( const_) ) ;
10151062 }
10161063 Some ( value)
@@ -1780,7 +1827,7 @@ impl<'tcx> VnState<'_, '_, 'tcx> {
17801827 /// If either [`Self::try_as_constant`] as [`Self::try_as_place`] succeeds,
17811828 /// returns that result as an [`Operand`].
17821829 fn try_as_operand ( & mut self , index : VnIndex , location : Location ) -> Option < Operand < ' tcx > > {
1783- if let Some ( const_) = self . try_as_constant ( index) {
1830+ if let Some ( const_) = self . try_as_constant ( index, location ) {
17841831 Some ( Operand :: Constant ( Box :: new ( const_) ) )
17851832 } else if let Some ( place) = self . try_as_place ( index, location, false ) {
17861833 self . reused_locals . insert ( place. local ) ;
@@ -1791,7 +1838,11 @@ impl<'tcx> VnState<'_, '_, 'tcx> {
17911838 }
17921839
17931840 /// If `index` is a `Value::Constant`, return the `Constant` to be put in the MIR.
1794- fn try_as_constant ( & mut self , index : VnIndex ) -> Option < ConstOperand < ' tcx > > {
1841+ fn try_as_constant (
1842+ & mut self ,
1843+ index : VnIndex ,
1844+ location : Location ,
1845+ ) -> Option < ConstOperand < ' tcx > > {
17951846 // This was already constant in MIR, do not change it. If the constant is not
17961847 // deterministic, adding an additional mention of it in MIR will not give the same value as
17971848 // the former mention.
@@ -1800,21 +1851,34 @@ impl<'tcx> VnState<'_, '_, 'tcx> {
18001851 return Some ( ConstOperand { span : DUMMY_SP , user_ty : None , const_ : value } ) ;
18011852 }
18021853
1803- let op = self . eval_to_const ( index) ?;
1804- if op. layout . is_unsized ( ) {
1805- // Do not attempt to propagate unsized locals.
1806- return None ;
1807- }
1854+ if let Some ( op) = self . eval_to_const ( index) {
1855+ if op. layout . is_unsized ( ) {
1856+ // Do not attempt to propagate unsized locals.
1857+ return None ;
1858+ }
1859+
1860+ let value = op_to_prop_const ( & mut self . ecx , op) ?;
18081861
1809- let value = op_to_prop_const ( & mut self . ecx , op) ?;
1862+ // Check that we do not leak a pointer.
1863+ // Those pointers may lose part of their identity in codegen.
1864+ // FIXME: remove this hack once https://github.com/rust-lang/rust/issues/79738 is fixed.
1865+ assert ! ( !value. may_have_provenance( self . tcx, op. layout. size) ) ;
18101866
1811- // Check that we do not leak a pointer.
1812- // Those pointers may lose part of their identity in codegen.
1813- // FIXME: remove this hack once https://github.com/rust-lang/rust/issues/79738 is fixed.
1814- assert ! ( !value. may_have_provenance( self . tcx, op. layout. size) ) ;
1867+ let const_ = Const :: Val ( value, op. layout . ty ) ;
1868+ return Some ( ConstOperand { span : DUMMY_SP , user_ty : None , const_ } ) ;
1869+ }
18151870
1816- let const_ = Const :: Val ( value, op. layout . ty ) ;
1817- Some ( ConstOperand { span : DUMMY_SP , user_ty : None , const_ } )
1871+ if let Some ( range) = self . get_range ( index, location)
1872+ && range. start == range. end
1873+ {
1874+ let ty = self . ty ( index) ;
1875+ let layout = self . ecx . layout_of ( ty) . ok ( ) ?;
1876+ let value = ConstValue :: Scalar ( Scalar :: from_uint ( range. start , layout. size ) ) ;
1877+ let const_ = Const :: Val ( value, self . ty ( index) ) ;
1878+ return Some ( ConstOperand { span : DUMMY_SP , user_ty : None , const_ } ) ;
1879+ }
1880+
1881+ None
18181882 }
18191883
18201884 /// Construct a place which holds the same value as `index` and for which all locals strictly
@@ -1891,7 +1955,7 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
18911955
18921956 let value = self . simplify_rvalue ( lhs, rvalue, location) ;
18931957 if let Some ( value) = value {
1894- if let Some ( const_) = self . try_as_constant ( value) {
1958+ if let Some ( const_) = self . try_as_constant ( value, location ) {
18951959 * rvalue = Rvalue :: Use ( Operand :: Constant ( Box :: new ( const_) ) ) ;
18961960 } else if let Some ( place) = self . try_as_place ( value, location, false )
18971961 && * rvalue != Rvalue :: Use ( Operand :: Move ( place) )
@@ -1920,14 +1984,68 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
19201984 }
19211985
19221986 fn visit_terminator ( & mut self , terminator : & mut Terminator < ' tcx > , location : Location ) {
1923- if let Terminator { kind : TerminatorKind :: Call { destination, .. } , .. } = terminator {
1924- if let Some ( local) = destination. as_local ( )
1925- && self . ssa . is_ssa ( local)
1926- {
1927- let ty = self . local_decls [ local] . ty ;
1928- let opaque = self . new_opaque ( ty) ;
1929- self . assign ( local, opaque) ;
1987+ match & mut terminator. kind {
1988+ TerminatorKind :: Call { destination, .. } => {
1989+ if let Some ( local) = destination. as_local ( )
1990+ && self . ssa . is_ssa ( local)
1991+ {
1992+ let ty = self . local_decls [ local] . ty ;
1993+ let opaque = self . new_opaque ( ty) ;
1994+ self . assign ( local, opaque) ;
1995+ }
1996+ }
1997+ TerminatorKind :: Assert { cond, expected, target, .. } => {
1998+ if let Some ( value) = self . simplify_operand ( cond, location) {
1999+ let successor = Location { block : * target, statement_index : 0 } ;
2000+ if location. block != successor. block
2001+ && self . unique_predecessors . contains ( successor. block )
2002+ {
2003+ let val = * expected as u128 ;
2004+ let range = WrappingRange { start : val, end : val } ;
2005+ self . insert_range ( value, successor, range) ;
2006+ }
2007+ }
2008+ }
2009+ TerminatorKind :: SwitchInt { discr, targets } => {
2010+ if let Some ( value) = self . simplify_operand ( discr, location) {
2011+ let mut distinct_targets: FxHashMap < BasicBlock , u8 > = FxHashMap :: default ( ) ;
2012+ for ( _, target) in targets. iter ( ) {
2013+ let targets = distinct_targets. entry ( target) . or_default ( ) ;
2014+ if * targets == 0 {
2015+ * targets = 1 ;
2016+ } else {
2017+ * targets = 2 ;
2018+ }
2019+ }
2020+ for ( val, target) in targets. iter ( ) {
2021+ if distinct_targets[ & target] != 1 {
2022+ continue ;
2023+ }
2024+ let successor = Location { block : target, statement_index : 0 } ;
2025+ if location. block != successor. block
2026+ && self . unique_predecessors . contains ( successor. block )
2027+ {
2028+ let range = WrappingRange { start : val, end : val } ;
2029+ self . insert_range ( value, successor, range) ;
2030+ }
2031+ }
2032+
2033+ let otherwise = Location { block : targets. otherwise ( ) , statement_index : 0 } ;
2034+ if self . ty ( value) . is_bool ( )
2035+ && let [ val] = targets. all_values ( )
2036+ && location. block != otherwise. block
2037+ && self . unique_predecessors . contains ( otherwise. block )
2038+ {
2039+ let range = if val. get ( ) == 0 {
2040+ WrappingRange { start : 1 , end : 1 }
2041+ } else {
2042+ WrappingRange { start : 0 , end : 0 }
2043+ } ;
2044+ self . insert_range ( value, otherwise, range) ;
2045+ }
2046+ }
19302047 }
2048+ _ => { }
19312049 }
19322050 // Terminators that can write to memory may invalidate (nested) derefs.
19332051 if terminator. kind . can_write_to_memory ( ) {
0 commit comments