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 used to be the case with
65+ //! `ConstValue ::Slice` which have a new pointer each time they are evaluated, and is still the
66+ //! case with valtrees that generate a new allocation each time they are used. This is checked by
67+ //! `is_deterministic` .
6868//!
6969//! Meanwhile, we want to be able to read indirect constants. For instance:
7070//! ```
8181//! may be non-deterministic. When that happens, we assign a disambiguator to ensure that we do not
8282//! merge the constants. See `duplicate_slice` test in `gvn.rs`.
8383//!
84- //! Second, when writing constants in MIR, we do not write `Const::Slice` or `Const`
85- //! that contain `AllocId`s.
84+ //! Conversely, some constants cannot cross crate boundaries, which could happen because of
85+ //! inlining. For instance, constants that contain a fn pointer (`AllocId` pointing to a
86+ //! `GlobalAlloc::Function`) point to a different symbol in each codegen unit. To avoid this,
87+ //! when writing constants in MIR, we do not write `Const`s that contain `AllocId`s. This is
88+ //! checked by `may_have_provenance`.
8689
8790use std:: borrow:: Cow ;
8891use std:: hash:: { Hash , Hasher } ;
@@ -103,7 +106,7 @@ use rustc_hir::def::DefKind;
103106use rustc_index:: bit_set:: DenseBitSet ;
104107use rustc_index:: { IndexVec , newtype_index} ;
105108use rustc_middle:: bug;
106- use rustc_middle:: mir:: interpret:: GlobalAlloc ;
109+ use rustc_middle:: mir:: interpret:: { AllocRange , GlobalAlloc } ;
107110use rustc_middle:: mir:: visit:: * ;
108111use rustc_middle:: mir:: * ;
109112use rustc_middle:: ty:: layout:: HasTypingEnv ;
@@ -486,7 +489,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
486489
487490 #[ instrument( level = "trace" , skip( self ) , ret) ]
488491 fn insert_constant ( & mut self , value : Const < ' tcx > ) -> VnIndex {
489- let ( index, new) = if value . is_deterministic ( ) {
492+ let ( index, new) = if is_deterministic ( value ) {
490493 // The constant is deterministic, no need to disambiguate.
491494 let constant = Value :: Constant { value, disambiguator : None } ;
492495 self . values . insert ( value. ty ( ) , constant)
@@ -529,14 +532,14 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
529532 fn insert_bool ( & mut self , flag : bool ) -> VnIndex {
530533 // Booleans are deterministic.
531534 let value = Const :: from_bool ( self . tcx , flag) ;
532- debug_assert ! ( value . is_deterministic( ) ) ;
535+ debug_assert ! ( is_deterministic( value ) ) ;
533536 self . insert ( self . tcx . types . bool , Value :: Constant { value, disambiguator : None } )
534537 }
535538
536539 fn insert_scalar ( & mut self , ty : Ty < ' tcx > , scalar : Scalar ) -> VnIndex {
537540 // Scalars are deterministic.
538541 let value = Const :: from_scalar ( self . tcx , scalar, ty) ;
539- debug_assert ! ( value . is_deterministic( ) ) ;
542+ debug_assert ! ( is_deterministic( value ) ) ;
540543 self . insert ( ty, Value :: Constant { value, disambiguator : None } )
541544 }
542545
@@ -1692,6 +1695,45 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
16921695 }
16931696}
16941697
1698+ /// Return true if any evaluation of this constant in the same MIR body
1699+ /// always returns the same value, taking into account even pointer identity tests.
1700+ ///
1701+ /// In other words, this answers: is "cloning" the `Const` ok?
1702+ fn is_deterministic ( c : Const < ' _ > ) -> bool {
1703+ // Primitive types cannot contain provenance and always have the same value.
1704+ if c. ty ( ) . is_primitive ( ) {
1705+ return true ;
1706+ }
1707+
1708+ match c {
1709+ // Some constants may generate fresh allocations for pointers they contain,
1710+ // so using the same constant twice can yield two different results.
1711+ // Notably, valtrees purposefully generate new allocations.
1712+ Const :: Ty ( ..) => false ,
1713+ // We do not know the contents, so don't attempt to do anything clever.
1714+ Const :: Unevaluated ( ..) => false ,
1715+ // When an evaluated constant contains provenance, it is encoded as an `AllocId`.
1716+ // Cloning the constant will reuse the same `AllocId`. If this is in the same MIR
1717+ // body, this same `AllocId` will result in the same pointer in codegen.
1718+ Const :: Val ( ..) => true ,
1719+ }
1720+ }
1721+
1722+ /// Check if a constant may contain provenance information.
1723+ /// Can return `true` even if there is no provenance.
1724+ fn may_have_provenance ( tcx : TyCtxt < ' _ > , value : ConstValue , size : Size ) -> bool {
1725+ match value {
1726+ ConstValue :: ZeroSized | ConstValue :: Scalar ( Scalar :: Int ( _) ) => return false ,
1727+ ConstValue :: Scalar ( Scalar :: Ptr ( ..) ) | ConstValue :: Slice { .. } => return true ,
1728+ ConstValue :: Indirect { alloc_id, offset } => !tcx
1729+ . global_alloc ( alloc_id)
1730+ . unwrap_memory ( )
1731+ . inner ( )
1732+ . provenance ( )
1733+ . range_empty ( AllocRange :: from ( offset..offset + size) , & tcx) ,
1734+ }
1735+ }
1736+
16951737fn op_to_prop_const < ' tcx > (
16961738 ecx : & mut InterpCx < ' tcx , DummyMachine > ,
16971739 op : & OpTy < ' tcx > ,
@@ -1767,7 +1809,16 @@ fn op_to_prop_const<'tcx>(
17671809 // Check that we do not leak a pointer.
17681810 // Those pointers may lose part of their identity in codegen.
17691811 // FIXME: remove this hack once https://github.com/rust-lang/rust/issues/79738 is fixed.
1770- if ecx. tcx . global_alloc ( alloc_id) . unwrap_memory ( ) . inner ( ) . provenance ( ) . ptrs ( ) . is_empty ( ) {
1812+ if ecx
1813+ . tcx
1814+ . global_alloc ( alloc_id)
1815+ . unwrap_memory ( )
1816+ . inner ( )
1817+ . provenance ( )
1818+ . provenances ( )
1819+ . next ( )
1820+ . is_none ( )
1821+ {
17711822 return Some ( value) ;
17721823 }
17731824
@@ -1823,7 +1874,7 @@ impl<'tcx> VnState<'_, '_, 'tcx> {
18231874 // Check that we do not leak a pointer.
18241875 // Those pointers may lose part of their identity in codegen.
18251876 // FIXME: remove this hack once https://github.com/rust-lang/rust/issues/79738 is fixed.
1826- assert ! ( !value . may_have_provenance( self . tcx, Some ( op. layout. size) ) ) ;
1877+ assert ! ( !may_have_provenance( self . tcx, value , op. layout. size) ) ;
18271878
18281879 Some ( Const :: Val ( value, op. layout . ty ) )
18291880 }
0 commit comments