@@ -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
@@ -429,6 +453,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
429453 debug_assert_eq ! ( index, _index) ;
430454 let _index = self . rev_locals . push ( SmallVec :: new ( ) ) ;
431455 debug_assert_eq ! ( index, _index) ;
456+ let _index = self . ranges . push ( Vec :: new ( ) ) ;
457+ debug_assert_eq ! ( index, _index) ;
432458 }
433459 index
434460 }
@@ -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 index
446474 }
447475
@@ -480,6 +508,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
480508 debug_assert_eq ! ( index, _index) ;
481509 let _index = self . rev_locals . push ( SmallVec :: new ( ) ) ;
482510 debug_assert_eq ! ( index, _index) ;
511+ let _index = self . ranges . push ( Vec :: new ( ) ) ;
512+ debug_assert_eq ! ( index, _index) ;
483513
484514 Some ( index)
485515 }
@@ -504,6 +534,8 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
504534 debug_assert_eq ! ( index, _index) ;
505535 let _index = self . rev_locals . push ( SmallVec :: new ( ) ) ;
506536 debug_assert_eq ! ( index, _index) ;
537+ let _index = self . ranges . push ( Vec :: new ( ) ) ;
538+ debug_assert_eq ! ( index, _index) ;
507539 }
508540 index
509541 }
@@ -526,6 +558,20 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
526558 self . rev_locals [ value] . push ( local) ;
527559 }
528560
561+ /// Create a new known range at the location.
562+ fn insert_range ( & mut self , value : VnIndex , location : Location , range : WrappingRange ) {
563+ self . ranges [ value] . push ( ( location, range) ) ;
564+ }
565+
566+ /// Get the known range at the location.
567+ fn get_range ( & self , value : VnIndex , location : Location ) -> Option < WrappingRange > {
568+ // FIXME: This should use the intersection of all valid ranges.
569+ let ( _, range) = self . ranges [ value]
570+ . iter ( )
571+ . find ( |( range_loc, _) | range_loc. dominates ( location, & self . dominators ) ) ?;
572+ Some ( * range)
573+ }
574+
529575 fn insert_bool ( & mut self , flag : bool ) -> VnIndex {
530576 // Booleans are deterministic.
531577 let value = Const :: from_bool ( self . tcx , flag) ;
@@ -1010,7 +1056,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
10101056 Operand :: Constant ( ref constant) => Some ( self . insert_constant ( constant. const_ ) ) ,
10111057 Operand :: Copy ( ref mut place) | Operand :: Move ( ref mut place) => {
10121058 let value = self . simplify_place_value ( place, location) ?;
1013- if let Some ( const_) = self . try_as_constant ( value) {
1059+ if let Some ( const_) = self . try_as_constant ( value, location ) {
10141060 * operand = Operand :: Constant ( Box :: new ( const_) ) ;
10151061 }
10161062 Some ( value)
@@ -1780,7 +1826,7 @@ impl<'tcx> VnState<'_, '_, 'tcx> {
17801826 /// If either [`Self::try_as_constant`] as [`Self::try_as_place`] succeeds,
17811827 /// returns that result as an [`Operand`].
17821828 fn try_as_operand ( & mut self , index : VnIndex , location : Location ) -> Option < Operand < ' tcx > > {
1783- if let Some ( const_) = self . try_as_constant ( index) {
1829+ if let Some ( const_) = self . try_as_constant ( index, location ) {
17841830 Some ( Operand :: Constant ( Box :: new ( const_) ) )
17851831 } else if let Some ( place) = self . try_as_place ( index, location, false ) {
17861832 self . reused_locals . insert ( place. local ) ;
@@ -1791,7 +1837,11 @@ impl<'tcx> VnState<'_, '_, 'tcx> {
17911837 }
17921838
17931839 /// 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 > > {
1840+ fn try_as_constant (
1841+ & mut self ,
1842+ index : VnIndex ,
1843+ location : Location ,
1844+ ) -> Option < ConstOperand < ' tcx > > {
17951845 // This was already constant in MIR, do not change it. If the constant is not
17961846 // deterministic, adding an additional mention of it in MIR will not give the same value as
17971847 // the former mention.
@@ -1800,21 +1850,34 @@ impl<'tcx> VnState<'_, '_, 'tcx> {
18001850 return Some ( ConstOperand { span : DUMMY_SP , user_ty : None , const_ : value } ) ;
18011851 }
18021852
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- }
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) ?;
18081860
1809- 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) ) ;
18101865
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) ) ;
1866+ let const_ = Const :: Val ( value, op. layout . ty ) ;
1867+ return Some ( ConstOperand { span : DUMMY_SP , user_ty : None , const_ } ) ;
1868+ }
18151869
1816- let const_ = Const :: Val ( value, op. layout . ty ) ;
1817- 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
18181881 }
18191882
18201883 /// Construct a place which holds the same value as `index` and for which all locals strictly
@@ -1891,7 +1954,7 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
18911954
18921955 let value = self . simplify_rvalue ( lhs, rvalue, location) ;
18931956 if let Some ( value) = value {
1894- if let Some ( const_) = self . try_as_constant ( value) {
1957+ if let Some ( const_) = self . try_as_constant ( value, location ) {
18951958 * rvalue = Rvalue :: Use ( Operand :: Constant ( Box :: new ( const_) ) ) ;
18961959 } else if let Some ( place) = self . try_as_place ( value, location, false )
18971960 && * rvalue != Rvalue :: Use ( Operand :: Move ( place) )
@@ -1920,14 +1983,68 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, '_, 'tcx> {
19201983 }
19211984
19221985 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) ;
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+ let successor = Location { block : * target, statement_index : 0 } ;
1999+ if location. block != successor. block
2000+ && self . unique_predecessors . contains ( successor. block )
2001+ {
2002+ let val = * expected as u128 ;
2003+ let range = WrappingRange { start : val, end : val } ;
2004+ self . insert_range ( value, successor, range) ;
2005+ }
2006+ }
2007+ }
2008+ TerminatorKind :: SwitchInt { discr, targets } => {
2009+ if let Some ( value) = self . simplify_operand ( discr, location) {
2010+ let mut distinct_targets: FxHashMap < BasicBlock , u8 > = FxHashMap :: default ( ) ;
2011+ for ( _, target) in targets. iter ( ) {
2012+ let targets = distinct_targets. entry ( target) . or_default ( ) ;
2013+ if * targets == 0 {
2014+ * targets = 1 ;
2015+ } else {
2016+ * targets = 2 ;
2017+ }
2018+ }
2019+ for ( val, target) in targets. iter ( ) {
2020+ if distinct_targets[ & target] != 1 {
2021+ continue ;
2022+ }
2023+ let successor = Location { block : target, statement_index : 0 } ;
2024+ if location. block != successor. block
2025+ && self . unique_predecessors . contains ( successor. block )
2026+ {
2027+ let range = WrappingRange { start : val, end : val } ;
2028+ self . insert_range ( value, successor, range) ;
2029+ }
2030+ }
2031+
2032+ let otherwise = Location { block : targets. otherwise ( ) , statement_index : 0 } ;
2033+ if self . ty ( value) . is_bool ( )
2034+ && let [ val] = targets. all_values ( )
2035+ && location. block != otherwise. block
2036+ && self . unique_predecessors . contains ( otherwise. block )
2037+ {
2038+ let range = if val. get ( ) == 0 {
2039+ WrappingRange { start : 1 , end : 1 }
2040+ } else {
2041+ WrappingRange { start : 0 , end : 0 }
2042+ } ;
2043+ self . insert_range ( value, otherwise, range) ;
2044+ }
2045+ }
19302046 }
2047+ _ => { }
19312048 }
19322049 // Terminators that can write to memory may invalidate (nested) derefs.
19332050 if terminator. kind . can_write_to_memory ( ) {
0 commit comments