6161//! The evaluated form is inserted in `evaluated` as an `OpTy` or `None` if evaluation failed.
6262//!
6363//! The difficulty is non-deterministic evaluation of MIR constants. Some `Const` can have
64- //! different runtime values each time they are evaluated. This is the case with
65- //! `Const::Slice` which have a new pointer each time they are evaluated, and constants that
66- //! contain a fn pointer (`AllocId` pointing to a `GlobalAlloc::Function`) pointing to a different
67- //! symbol in each codegen unit.
64+ //! different runtime values each time they are evaluated. This happens with valtrees that
65+ //! generate a new allocation each time they are used. This is checked by `is_deterministic`.
6866//!
6967//! Meanwhile, we want to be able to read indirect constants. For instance:
7068//! ```
8179//! may be non-deterministic. When that happens, we assign a disambiguator to ensure that we do not
8280//! merge the constants. See `duplicate_slice` test in `gvn.rs`.
8381//!
84- //! Second, when writing constants in MIR, we do not write `Const::Slice` or `Const`
85- //! that contain `AllocId`s.
82+ //! Conversely, some constants cannot cross function boundaries, which could happen because of
83+ //! inlining. For instance, constants that contain a fn pointer (`AllocId` pointing to a
84+ //! `GlobalAlloc::Function`) point to a different symbol in each codegen unit. To avoid this,
85+ //! when writing constants in MIR, we do not write `Const`s that contain `AllocId`s. This is
86+ //! checked by `may_have_provenance`. See <https://github.com/rust-lang/rust/issues/128775> for
87+ //! more information.
8688
8789use std:: borrow:: Cow ;
8890use std:: hash:: { Hash , Hasher } ;
@@ -103,7 +105,7 @@ use rustc_hir::def::DefKind;
103105use rustc_index:: bit_set:: DenseBitSet ;
104106use rustc_index:: { IndexVec , newtype_index} ;
105107use rustc_middle:: bug;
106- use rustc_middle:: mir:: interpret:: GlobalAlloc ;
108+ use rustc_middle:: mir:: interpret:: { AllocRange , GlobalAlloc } ;
107109use rustc_middle:: mir:: visit:: * ;
108110use rustc_middle:: mir:: * ;
109111use rustc_middle:: ty:: layout:: HasTypingEnv ;
@@ -487,7 +489,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
487489
488490 #[ instrument( level = "trace" , skip( self ) , ret) ]
489491 fn insert_constant ( & mut self , value : Const < ' tcx > ) -> VnIndex {
490- if value . is_deterministic ( ) {
492+ if is_deterministic ( value ) {
491493 // The constant is deterministic, no need to disambiguate.
492494 let constant = Value :: Constant { value, disambiguator : None } ;
493495 self . insert ( value. ty ( ) , constant)
@@ -522,14 +524,14 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
522524 fn insert_bool ( & mut self , flag : bool ) -> VnIndex {
523525 // Booleans are deterministic.
524526 let value = Const :: from_bool ( self . tcx , flag) ;
525- debug_assert ! ( value . is_deterministic( ) ) ;
527+ debug_assert ! ( is_deterministic( value ) ) ;
526528 self . insert ( self . tcx . types . bool , Value :: Constant { value, disambiguator : None } )
527529 }
528530
529531 fn insert_scalar ( & mut self , ty : Ty < ' tcx > , scalar : Scalar ) -> VnIndex {
530532 // Scalars are deterministic.
531533 let value = Const :: from_scalar ( self . tcx , scalar, ty) ;
532- debug_assert ! ( value . is_deterministic( ) ) ;
534+ debug_assert ! ( is_deterministic( value ) ) ;
533535 self . insert ( ty, Value :: Constant { value, disambiguator : None } )
534536 }
535537
@@ -1002,21 +1004,19 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
10021004 operand : & mut Operand < ' tcx > ,
10031005 location : Location ,
10041006 ) -> Option < VnIndex > {
1005- match * operand {
1006- Operand :: RuntimeChecks ( c) => {
1007- Some ( self . insert ( self . tcx . types . bool , Value :: RuntimeChecks ( c) ) )
1008- }
1009- Operand :: Constant ( ref constant) => Some ( self . insert_constant ( constant. const_ ) ) ,
1007+ let value = match * operand {
1008+ Operand :: RuntimeChecks ( c) => self . insert ( self . tcx . types . bool , Value :: RuntimeChecks ( c) ) ,
1009+ Operand :: Constant ( ref constant) => self . insert_constant ( constant. const_ ) ,
10101010 Operand :: Copy ( ref mut place) | Operand :: Move ( ref mut place) => {
1011- let value = self . simplify_place_value ( place, location) ?;
1012- if let Some ( const_) = self . try_as_constant ( value) {
1013- * operand = Operand :: Constant ( Box :: new ( const_) ) ;
1014- } else if let Value :: RuntimeChecks ( c) = self . get ( value) {
1015- * operand = Operand :: RuntimeChecks ( c) ;
1016- }
1017- Some ( value)
1011+ self . simplify_place_value ( place, location) ?
10181012 }
1013+ } ;
1014+ if let Some ( const_) = self . try_as_constant ( value) {
1015+ * operand = Operand :: Constant ( Box :: new ( const_) ) ;
1016+ } else if let Value :: RuntimeChecks ( c) = self . get ( value) {
1017+ * operand = Operand :: RuntimeChecks ( c) ;
10191018 }
1019+ Some ( value)
10201020 }
10211021
10221022 #[ instrument( level = "trace" , skip( self ) , ret) ]
@@ -1731,6 +1731,49 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
17311731 }
17321732}
17331733
1734+ /// Return true if any evaluation of this constant in the same MIR body
1735+ /// always returns the same value, taking into account even pointer identity tests.
1736+ ///
1737+ /// In other words, this answers: is "cloning" the `Const` ok?
1738+ ///
1739+ /// This returns `false` for constants that synthesize new `AllocId` when they are instantiated.
1740+ /// It is `true` for anything else, since a given `AllocId` *does* have a unique runtime value
1741+ /// within the scope of a single MIR body.
1742+ fn is_deterministic ( c : Const < ' _ > ) -> bool {
1743+ // Primitive types cannot contain provenance and always have the same value.
1744+ if c. ty ( ) . is_primitive ( ) {
1745+ return true ;
1746+ }
1747+
1748+ match c {
1749+ // Some constants may generate fresh allocations for pointers they contain,
1750+ // so using the same constant twice can yield two different results.
1751+ // Notably, valtrees purposefully generate new allocations.
1752+ Const :: Ty ( ..) => false ,
1753+ // We do not know the contents, so don't attempt to do anything clever.
1754+ Const :: Unevaluated ( ..) => false ,
1755+ // When an evaluated constant contains provenance, it is encoded as an `AllocId`.
1756+ // Cloning the constant will reuse the same `AllocId`. If this is in the same MIR
1757+ // body, this same `AllocId` will result in the same pointer in codegen.
1758+ Const :: Val ( ..) => true ,
1759+ }
1760+ }
1761+
1762+ /// Check if a constant may contain provenance information.
1763+ /// Can return `true` even if there is no provenance.
1764+ fn may_have_provenance ( tcx : TyCtxt < ' _ > , value : ConstValue , size : Size ) -> bool {
1765+ match value {
1766+ ConstValue :: ZeroSized | ConstValue :: Scalar ( Scalar :: Int ( _) ) => return false ,
1767+ ConstValue :: Scalar ( Scalar :: Ptr ( ..) ) | ConstValue :: Slice { .. } => return true ,
1768+ ConstValue :: Indirect { alloc_id, offset } => !tcx
1769+ . global_alloc ( alloc_id)
1770+ . unwrap_memory ( )
1771+ . inner ( )
1772+ . provenance ( )
1773+ . range_empty ( AllocRange :: from ( offset..offset + size) , & tcx) ,
1774+ }
1775+ }
1776+
17341777fn op_to_prop_const < ' tcx > (
17351778 ecx : & mut InterpCx < ' tcx , DummyMachine > ,
17361779 op : & OpTy < ' tcx > ,
@@ -1762,7 +1805,7 @@ fn op_to_prop_const<'tcx>(
17621805 if !scalar. try_to_scalar_int ( ) . is_ok ( ) {
17631806 // Check that we do not leak a pointer.
17641807 // Those pointers may lose part of their identity in codegen.
1765- // FIXME: remove this hack once https://github.com/rust-lang/rust/issues/79738 is fixed.
1808+ // FIXME: remove this hack once https://github.com/rust-lang/rust/issues/128775 is fixed.
17661809 return None ;
17671810 }
17681811 return Some ( ConstValue :: Scalar ( scalar) ) ;
@@ -1774,7 +1817,7 @@ fn op_to_prop_const<'tcx>(
17741817 let ( size, _align) = ecx. size_and_align_of_val ( & mplace) . discard_err ( ) ??;
17751818
17761819 // Do not try interning a value that contains provenance.
1777- // Due to https://github.com/rust-lang/rust/issues/79738 , doing so could lead to bugs.
1820+ // Due to https://github.com/rust-lang/rust/issues/128775 , doing so could lead to bugs.
17781821 // FIXME: remove this hack once that issue is fixed.
17791822 let alloc_ref = ecx. get_ptr_alloc ( mplace. ptr ( ) , size) . discard_err ( ) ??;
17801823 if alloc_ref. has_provenance ( ) {
@@ -1801,16 +1844,7 @@ fn op_to_prop_const<'tcx>(
18011844 // Everything failed: create a new allocation to hold the data.
18021845 let alloc_id =
18031846 ecx. intern_with_temp_alloc ( op. layout , |ecx, dest| ecx. copy_op ( op, dest) ) . discard_err ( ) ?;
1804- let value = ConstValue :: Indirect { alloc_id, offset : Size :: ZERO } ;
1805-
1806- // Check that we do not leak a pointer.
1807- // Those pointers may lose part of their identity in codegen.
1808- // FIXME: remove this hack once https://github.com/rust-lang/rust/issues/79738 is fixed.
1809- if ecx. tcx . global_alloc ( alloc_id) . unwrap_memory ( ) . inner ( ) . provenance ( ) . ptrs ( ) . is_empty ( ) {
1810- return Some ( value) ;
1811- }
1812-
1813- None
1847+ Some ( ConstValue :: Indirect { alloc_id, offset : Size :: ZERO } )
18141848}
18151849
18161850impl < ' tcx > VnState < ' _ , ' _ , ' tcx > {
@@ -1831,14 +1865,28 @@ impl<'tcx> VnState<'_, '_, 'tcx> {
18311865
18321866 /// If `index` is a `Value::Constant`, return the `Constant` to be put in the MIR.
18331867 fn try_as_constant ( & mut self , index : VnIndex ) -> Option < ConstOperand < ' tcx > > {
1834- // This was already constant in MIR, do not change it. If the constant is not
1835- // deterministic, adding an additional mention of it in MIR will not give the same value as
1836- // the former mention.
1837- if let Value :: Constant { value, disambiguator : None } = self . get ( index) {
1838- debug_assert ! ( value. is_deterministic( ) ) ;
1868+ let value = self . get ( index) ;
1869+
1870+ // This was already an *evaluated* constant in MIR, do not change it.
1871+ if let Value :: Constant { value, disambiguator : None } = value
1872+ && let Const :: Val ( ..) = value
1873+ {
18391874 return Some ( ConstOperand { span : DUMMY_SP , user_ty : None , const_ : value } ) ;
18401875 }
18411876
1877+ if let Some ( value) = self . try_as_evaluated_constant ( index) {
1878+ return Some ( ConstOperand { span : DUMMY_SP , user_ty : None , const_ : value } ) ;
1879+ }
1880+
1881+ // We failed to provide an evaluated form, fallback to using the unevaluated constant.
1882+ if let Value :: Constant { value, disambiguator : None } = value {
1883+ return Some ( ConstOperand { span : DUMMY_SP , user_ty : None , const_ : value } ) ;
1884+ }
1885+
1886+ None
1887+ }
1888+
1889+ fn try_as_evaluated_constant ( & mut self , index : VnIndex ) -> Option < Const < ' tcx > > {
18421890 let op = self . eval_to_const ( index) ?;
18431891 if op. layout . is_unsized ( ) {
18441892 // Do not attempt to propagate unsized locals.
@@ -1849,11 +1897,12 @@ impl<'tcx> VnState<'_, '_, 'tcx> {
18491897
18501898 // Check that we do not leak a pointer.
18511899 // Those pointers may lose part of their identity in codegen.
1852- // FIXME: remove this hack once https://github.com/rust-lang/rust/issues/79738 is fixed.
1853- assert ! ( !value. may_have_provenance( self . tcx, op. layout. size) ) ;
1900+ // FIXME: remove this hack once https://github.com/rust-lang/rust/issues/128775 is fixed.
1901+ if may_have_provenance ( self . tcx , value, op. layout . size ) {
1902+ return None ;
1903+ }
18541904
1855- let const_ = Const :: Val ( value, op. layout . ty ) ;
1856- Some ( ConstOperand { span : DUMMY_SP , user_ty : None , const_ } )
1905+ Some ( Const :: Val ( value, op. layout . ty ) )
18571906 }
18581907
18591908 /// Construct a place which holds the same value as `index` and for which all locals strictly
0 commit comments