@@ -90,14 +90,16 @@ use std::hash::{Hash, Hasher};
9090use either:: Either ;
9191use hashbrown:: hash_table:: { Entry , HashTable } ;
9292use itertools:: Itertools as _;
93- use rustc_abi:: { self as abi, BackendRepr , FIRST_VARIANT , FieldIdx , Primitive , Size , VariantIdx } ;
93+ use rustc_abi:: {
94+ self as abi, BackendRepr , FIRST_VARIANT , FieldIdx , Primitive , Size , VariantIdx , WrappingRange ,
95+ } ;
9496use rustc_arena:: DroplessArena ;
9597use rustc_const_eval:: const_eval:: DummyMachine ;
9698use rustc_const_eval:: interpret:: {
9799 ImmTy , Immediate , InterpCx , MemPlaceMeta , MemoryKind , OpTy , Projectable , Scalar ,
98100 intern_const_alloc_for_constprop,
99101} ;
100- use rustc_data_structures:: fx:: FxHasher ;
102+ use rustc_data_structures:: fx:: { FxHashMap , FxHasher } ;
101103use rustc_data_structures:: graph:: dominators:: Dominators ;
102104use rustc_hir:: def:: DefKind ;
103105use rustc_index:: bit_set:: DenseBitSet ;
@@ -130,10 +132,25 @@ impl<'tcx> crate::MirPass<'tcx> for GVN {
130132 // Clone dominators because we need them while mutating the body.
131133 let dominators = body. basic_blocks . dominators ( ) . clone ( ) ;
132134 let maybe_loop_headers = loops:: maybe_loop_headers ( body) ;
135+ let predecessors = body. basic_blocks . predecessors ( ) ;
136+ let mut unique_predecessors = DenseBitSet :: new_empty ( body. basic_blocks . len ( ) ) ;
137+ for ( bb, _) in body. basic_blocks . iter_enumerated ( ) {
138+ if predecessors[ bb] . len ( ) == 1 {
139+ unique_predecessors. insert ( bb) ;
140+ }
141+ }
133142
134143 let arena = DroplessArena :: default ( ) ;
135- let mut state =
136- VnState :: new ( tcx, body, typing_env, & ssa, dominators, & body. local_decls , & arena) ;
144+ let mut state = VnState :: new (
145+ tcx,
146+ body,
147+ typing_env,
148+ & ssa,
149+ dominators,
150+ & body. local_decls ,
151+ & arena,
152+ unique_predecessors,
153+ ) ;
137154
138155 for local in body. args_iter ( ) . filter ( |& local| ssa. is_ssa ( local) ) {
139156 let opaque = state. new_opaque ( body. local_decls [ local] . ty ) ;
@@ -380,6 +397,10 @@ struct VnState<'body, 'a, 'tcx> {
380397 dominators : Dominators < BasicBlock > ,
381398 reused_locals : DenseBitSet < Local > ,
382399 arena : & ' a DroplessArena ,
400+ /// Known ranges at each locations.
401+ ranges : IndexVec < VnIndex , Vec < ( Location , WrappingRange ) > > ,
402+ /// Determines if the basic block has a single unique predecessor.
403+ unique_predecessors : DenseBitSet < BasicBlock > ,
383404}
384405
385406impl < ' body , ' a , ' tcx > VnState < ' body , ' a , ' tcx > {
@@ -391,6 +412,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
391412 dominators : Dominators < BasicBlock > ,
392413 local_decls : & ' body LocalDecls < ' tcx > ,
393414 arena : & ' a DroplessArena ,
415+ unique_predecessors : DenseBitSet < BasicBlock > ,
394416 ) -> Self {
395417 // Compute a rough estimate of the number of values in the body from the number of
396418 // statements. This is meant to reduce the number of allocations, but it's all right if
@@ -413,6 +435,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
413435 dominators,
414436 reused_locals : DenseBitSet :: new_empty ( local_decls. len ( ) ) ,
415437 arena,
438+ ranges : IndexVec :: with_capacity ( num_values) ,
439+ unique_predecessors,
416440 }
417441 }
418442
@@ -430,6 +454,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
430454 debug_assert_eq ! ( index, _index) ;
431455 let _index = self . rev_locals . push ( SmallVec :: new ( ) ) ;
432456 debug_assert_eq ! ( index, _index) ;
457+ let _index = self . ranges . push ( Vec :: new ( ) ) ;
458+ debug_assert_eq ! ( index, _index) ;
433459 index
434460 }
435461
@@ -442,6 +468,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
442468 debug_assert_eq ! ( index, _index) ;
443469 let _index = self . rev_locals . push ( SmallVec :: new ( ) ) ;
444470 debug_assert_eq ! ( index, _index) ;
471+ let _index = self . ranges . push ( Vec :: new ( ) ) ;
472+ debug_assert_eq ! ( index, _index) ;
445473 }
446474 index
447475 }
@@ -523,6 +551,20 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
523551 self . rev_locals [ value] . push ( local) ;
524552 }
525553
554+ /// Create a new known range at the location.
555+ fn insert_range ( & mut self , value : VnIndex , location : Location , range : WrappingRange ) {
556+ self . ranges [ value] . push ( ( location, range) ) ;
557+ }
558+
559+ /// Get the known range at the location.
560+ fn get_range ( & self , value : VnIndex , location : Location ) -> Option < WrappingRange > {
561+ // FIXME: This should use the intersection of all valid ranges.
562+ let ( _, range) = self . ranges [ value]
563+ . iter ( )
564+ . find ( |( range_loc, _) | range_loc. dominates ( location, & self . dominators ) ) ?;
565+ Some ( * range)
566+ }
567+
526568 fn insert_bool ( & mut self , flag : bool ) -> VnIndex {
527569 // Booleans are deterministic.
528570 let value = Const :: from_bool ( self . tcx , flag) ;
@@ -1011,7 +1053,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
10111053 Operand :: Constant ( ref constant) => Some ( self . insert_constant ( constant. const_ ) ) ,
10121054 Operand :: Copy ( ref mut place) | Operand :: Move ( ref mut place) => {
10131055 let value = self . simplify_place_value ( place, location) ?;
1014- if let Some ( const_) = self . try_as_constant ( value) {
1056+ if let Some ( const_) = self . try_as_constant ( value, location ) {
10151057 * operand = Operand :: Constant ( Box :: new ( const_) ) ;
10161058 } else if let Value :: RuntimeChecks ( c) = self . get ( value) {
10171059 * operand = Operand :: RuntimeChecks ( c) ;
@@ -1782,7 +1824,7 @@ impl<'tcx> VnState<'_, '_, 'tcx> {
17821824 /// If either [`Self::try_as_constant`] as [`Self::try_as_place`] succeeds,
17831825 /// returns that result as an [`Operand`].
17841826 fn try_as_operand ( & mut self , index : VnIndex , location : Location ) -> Option < Operand < ' tcx > > {
1785- if let Some ( const_) = self . try_as_constant ( index) {
1827+ if let Some ( const_) = self . try_as_constant ( index, location ) {
17861828 Some ( Operand :: Constant ( Box :: new ( const_) ) )
17871829 } else if let Value :: RuntimeChecks ( c) = self . get ( index) {
17881830 Some ( Operand :: RuntimeChecks ( c) )
@@ -1795,7 +1837,11 @@ impl<'tcx> VnState<'_, '_, 'tcx> {
17951837 }
17961838
17971839 /// If `index` is a `Value::Constant`, return the `Constant` to be put in the MIR.
1798- fn try_as_constant ( & mut self , index : VnIndex ) -> Option < ConstOperand < ' tcx > > {
1840+ fn try_as_constant (
1841+ & mut self ,
1842+ index : VnIndex ,
1843+ location : Location ,
1844+ ) -> Option < ConstOperand < ' tcx > > {
17991845 // This was already constant in MIR, do not change it. If the constant is not
18001846 // deterministic, adding an additional mention of it in MIR will not give the same value as
18011847 // the former mention.
@@ -1804,21 +1850,34 @@ impl<'tcx> VnState<'_, '_, 'tcx> {
18041850 return Some ( ConstOperand { span : DUMMY_SP , user_ty : None , const_ : value } ) ;
18051851 }
18061852
1807- let op = self . eval_to_const ( index) ?;
1808- if op. layout . is_unsized ( ) {
1809- // Do not attempt to propagate unsized locals.
1810- return None ;
1811- }
1853+ if let Some ( op) = self . eval_to_const ( index) {
1854+ if op. layout . is_unsized ( ) {
1855+ // Do not attempt to propagate unsized locals.
1856+ return None ;
1857+ }
1858+
1859+ let value = op_to_prop_const ( & mut self . ecx , op) ?;
18121860
1813- let value = op_to_prop_const ( & mut self . ecx , op) ?;
1861+ // Check that we do not leak a pointer.
1862+ // Those pointers may lose part of their identity in codegen.
1863+ // FIXME: remove this hack once https://github.com/rust-lang/rust/issues/79738 is fixed.
1864+ assert ! ( !value. may_have_provenance( self . tcx, op. layout. size) ) ;
18141865
1815- // Check that we do not leak a pointer.
1816- // Those pointers may lose part of their identity in codegen.
1817- // FIXME: remove this hack once https://github.com/rust-lang/rust/issues/79738 is fixed.
1818- assert ! ( !value. may_have_provenance( self . tcx, op. layout. size) ) ;
1866+ let const_ = Const :: Val ( value, op. layout . ty ) ;
1867+ return Some ( ConstOperand { span : DUMMY_SP , user_ty : None , const_ } ) ;
1868+ }
18191869
1820- let const_ = Const :: Val ( value, op. layout . ty ) ;
1821- Some ( ConstOperand { span : DUMMY_SP , user_ty : None , const_ } )
1870+ if let Some ( range) = self . get_range ( index, location)
1871+ && range. start == range. end
1872+ {
1873+ let ty = self . ty ( index) ;
1874+ let layout = self . ecx . layout_of ( ty) . ok ( ) ?;
1875+ let value = ConstValue :: Scalar ( Scalar :: from_uint ( range. start , layout. size ) ) ;
1876+ let const_ = Const :: Val ( value, self . ty ( index) ) ;
1877+ return Some ( ConstOperand { span : DUMMY_SP , user_ty : None , const_ } ) ;
1878+ }
1879+
1880+ None
18221881 }
18231882
18241883 /// Construct a place which holds the same value as `index` and for which all locals strictly
@@ -1895,7 +1954,7 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
18951954
18961955 let value = self . simplify_rvalue ( lhs, rvalue, location) ;
18971956 if let Some ( value) = value {
1898- if let Some ( const_) = self . try_as_constant ( value) {
1957+ if let Some ( const_) = self . try_as_constant ( value, location ) {
18991958 * rvalue = Rvalue :: Use ( Operand :: Constant ( Box :: new ( const_) ) ) ;
19001959 } else if let Some ( place) = self . try_as_place ( value, location, false )
19011960 && * rvalue != Rvalue :: Use ( Operand :: Move ( place) )
@@ -1924,14 +1983,73 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
19241983 }
19251984
19261985 fn visit_terminator ( & mut self , terminator : & mut Terminator < ' tcx > , location : Location ) {
1927- if let Terminator { kind : TerminatorKind :: Call { destination, .. } , .. } = terminator {
1928- if let Some ( local) = destination. as_local ( )
1929- && self . ssa . is_ssa ( local)
1930- {
1931- let ty = self . local_decls [ local] . ty ;
1932- let opaque = self . new_opaque ( ty) ;
1933- self . assign ( local, opaque) ;
1986+ match & mut terminator. kind {
1987+ TerminatorKind :: Call { destination, .. } => {
1988+ if let Some ( local) = destination. as_local ( )
1989+ && self . ssa . is_ssa ( local)
1990+ {
1991+ let ty = self . local_decls [ local] . ty ;
1992+ let opaque = self . new_opaque ( ty) ;
1993+ self . assign ( local, opaque) ;
1994+ }
1995+ }
1996+ TerminatorKind :: Assert { cond, expected, target, .. } => {
1997+ if let Some ( value) = self . simplify_operand ( cond, location)
1998+ && !matches ! ( self . get( value) , Value :: Constant { .. } )
1999+ {
2000+ let successor = Location { block : * target, statement_index : 0 } ;
2001+ if location. block != successor. block
2002+ && self . unique_predecessors . contains ( successor. block )
2003+ {
2004+ let val = * expected as u128 ;
2005+ let range = WrappingRange { start : val, end : val } ;
2006+ self . insert_range ( value, successor, range) ;
2007+ }
2008+ }
2009+ }
2010+ TerminatorKind :: SwitchInt { discr, targets } => {
2011+ if let Some ( value) = self . simplify_operand ( discr, location)
2012+ && targets. all_targets ( ) . len ( ) < 16
2013+ && !matches ! ( self . get( value) , Value :: Constant { .. } )
2014+ {
2015+ let mut distinct_targets: FxHashMap < BasicBlock , u8 > = FxHashMap :: default ( ) ;
2016+ for ( _, target) in targets. iter ( ) {
2017+ let targets = distinct_targets. entry ( target) . or_default ( ) ;
2018+ if * targets == 0 {
2019+ * targets = 1 ;
2020+ } else {
2021+ * targets = 2 ;
2022+ }
2023+ }
2024+ for ( val, target) in targets. iter ( ) {
2025+ if distinct_targets[ & target] != 1 {
2026+ continue ;
2027+ }
2028+ let successor = Location { block : target, statement_index : 0 } ;
2029+ if location. block != successor. block
2030+ && self . unique_predecessors . contains ( successor. block )
2031+ {
2032+ let range = WrappingRange { start : val, end : val } ;
2033+ self . insert_range ( value, successor, range) ;
2034+ }
2035+ }
2036+
2037+ let otherwise = Location { block : targets. otherwise ( ) , statement_index : 0 } ;
2038+ if self . ty ( value) . is_bool ( )
2039+ && let [ val] = targets. all_values ( )
2040+ && location. block != otherwise. block
2041+ && self . unique_predecessors . contains ( otherwise. block )
2042+ {
2043+ let range = if val. get ( ) == 0 {
2044+ WrappingRange { start : 1 , end : 1 }
2045+ } else {
2046+ WrappingRange { start : 0 , end : 0 }
2047+ } ;
2048+ self . insert_range ( value, otherwise, range) ;
2049+ }
2050+ }
19342051 }
2052+ _ => { }
19352053 }
19362054 // Terminators that can write to memory may invalidate (nested) derefs.
19372055 if terminator. kind . can_write_to_memory ( ) {
0 commit comments