5252//! _a = *_b // _b is &Freeze
5353//! _c = *_b // replaced by _c = _a
5454//! ```
55+ //!
56+ //! # Determinism of constant propagation
57+ //!
58+ //! When registering a new `Value`, we attempt to opportunistically evaluate it as a constant.
59+ //! The evaluated form is inserted in `evaluated` as an `OpTy` or `None` if evaluation failed.
60+ //!
61+ //! The difficulty is non-deterministic evaluation of MIR constants. Some `Const` can have
62+ //! different runtime values each time they are evaluated. This is the case with
63+ //! `Const::Slice` which have a new pointer each time they are evaluated, and constants that
64+ //! contain a fn pointer (`AllocId` pointing to a `GlobalAlloc::Function`) pointing to a different
65+ //! symbol in each codegen unit.
66+ //!
67+ //! Meanwhile, we want to be able to read indirect constants. For instance:
68+ //! ```
69+ //! static A: &'static &'static u8 = &&63;
70+ //! fn foo() -> u8 {
71+ //! **A // We want to replace by 63.
72+ //! }
73+ //! fn bar() -> u8 {
74+ //! b"abc"[1] // We want to replace by 'b'.
75+ //! }
76+ //! ```
77+ //!
78+ //! The `Value::Constant` variant stores a possibly unevaluated constant. Evaluating that constant
79+ //! may be non-deterministic. When that happens, we assign a disambiguator to ensure that we do not
80+ //! merge the constants. See `duplicate_slice` test in `gvn.rs`.
81+ //!
82+ //! Second, when writing constants in MIR, we do not write `Const::Slice` or `Const`
83+ //! that contain `AllocId`s.
5584
5685use rustc_const_eval:: interpret:: { intern_const_alloc_for_constprop, MemoryKind } ;
5786use rustc_const_eval:: interpret:: { ImmTy , InterpCx , OpTy , Projectable , Scalar } ;
@@ -161,7 +190,12 @@ enum Value<'tcx> {
161190 /// The `usize` is a counter incremented by `new_opaque`.
162191 Opaque ( usize ) ,
163192 /// Evaluated or unevaluated constant value.
164- Constant ( Const < ' tcx > ) ,
193+ Constant {
194+ value : Const < ' tcx > ,
195+ /// Some constants do not have a deterministic value. To avoid merging two instances of the
196+ /// same `Const`, we assign them an additional integer index.
197+ disambiguator : usize ,
198+ } ,
165199 /// An aggregate value, either tuple/closure/struct/enum.
166200 /// This does not contain unions, as we cannot reason with the value.
167201 Aggregate ( AggregateTy < ' tcx > , VariantIdx , Vec < VnIndex > ) ,
@@ -288,8 +322,24 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
288322 }
289323 }
290324
325+ fn insert_constant ( & mut self , value : Const < ' tcx > ) -> Option < VnIndex > {
326+ let disambiguator = if value. is_deterministic ( ) {
327+ // The constant is deterministic, no need to disambiguate.
328+ 0
329+ } else {
330+ // Multiple mentions of this constant will yield different values,
331+ // so assign a different `disambiguator` to ensure they do not get the same `VnIndex`.
332+ let next_opaque = self . next_opaque . as_mut ( ) ?;
333+ let disambiguator = * next_opaque;
334+ * next_opaque += 1 ;
335+ disambiguator
336+ } ;
337+ Some ( self . insert ( Value :: Constant { value, disambiguator } ) )
338+ }
339+
291340 fn insert_scalar ( & mut self , scalar : Scalar , ty : Ty < ' tcx > ) -> VnIndex {
292- self . insert ( Value :: Constant ( Const :: from_scalar ( self . tcx , scalar, ty) ) )
341+ self . insert_constant ( Const :: from_scalar ( self . tcx , scalar, ty) )
342+ . expect ( "scalars are deterministic" )
293343 }
294344
295345 #[ instrument( level = "trace" , skip( self ) , ret) ]
@@ -300,7 +350,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
300350 // Do not bother evaluating repeat expressions. This would uselessly consume memory.
301351 Repeat ( ..) => return None ,
302352
303- Constant ( ref constant) => self . ecx . eval_mir_constant ( constant, None , None ) . ok ( ) ?,
353+ Constant { ref value, disambiguator : _ } => {
354+ self . ecx . eval_mir_constant ( value, None , None ) . ok ( ) ?
355+ }
304356 Aggregate ( kind, variant, ref fields) => {
305357 let fields = fields
306358 . iter ( )
@@ -657,7 +709,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
657709 match * operand {
658710 Operand :: Constant ( ref mut constant) => {
659711 let const_ = constant. const_ . normalize ( self . tcx , self . param_env ) ;
660- Some ( self . insert ( Value :: Constant ( const_) ) )
712+ self . insert_constant ( const_)
661713 }
662714 Operand :: Copy ( ref mut place) | Operand :: Move ( ref mut place) => {
663715 let value = self . simplify_place_value ( place, location) ?;
@@ -691,7 +743,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
691743 Value :: Repeat ( op, amount)
692744 }
693745 Rvalue :: NullaryOp ( op, ty) => Value :: NullaryOp ( op, ty) ,
694- Rvalue :: Aggregate ( ..) => self . simplify_aggregate ( rvalue, location) ? ,
746+ Rvalue :: Aggregate ( ..) => return self . simplify_aggregate ( rvalue, location) ,
695747 Rvalue :: Ref ( _, borrow_kind, ref mut place) => {
696748 self . simplify_place_projection ( place, location) ;
697749 return self . new_pointer ( * place, AddressKind :: Ref ( borrow_kind) ) ;
@@ -757,7 +809,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
757809 & mut self ,
758810 rvalue : & mut Rvalue < ' tcx > ,
759811 location : Location ,
760- ) -> Option < Value < ' tcx > > {
812+ ) -> Option < VnIndex > {
761813 let Rvalue :: Aggregate ( box ref kind, ref mut fields) = * rvalue else { bug ! ( ) } ;
762814
763815 let tcx = self . tcx ;
@@ -774,8 +826,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
774826
775827 if is_zst {
776828 let ty = rvalue. ty ( self . local_decls , tcx) ;
777- let value = Value :: Constant ( Const :: zero_sized ( ty) ) ;
778- return Some ( value) ;
829+ return self . insert_constant ( Const :: zero_sized ( ty) ) ;
779830 }
780831 }
781832
@@ -814,12 +865,11 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
814865 * rvalue = Rvalue :: Repeat ( Operand :: Copy ( local. into ( ) ) , len) ;
815866 self . reused_locals . insert ( local) ;
816867 }
817- return Some ( Value :: Repeat ( first, len) ) ;
868+ return Some ( self . insert ( Value :: Repeat ( first, len) ) ) ;
818869 }
819870 }
820871
821- let value = Value :: Aggregate ( ty, variant_index, fields) ;
822- Some ( value)
872+ Some ( self . insert ( Value :: Aggregate ( ty, variant_index, fields) ) )
823873 }
824874}
825875
@@ -882,39 +932,12 @@ impl<'tcx> VnState<'_, 'tcx> {
882932 /// If `index` is a `Value::Constant`, return the `Constant` to be put in the MIR.
883933 fn try_as_constant ( & mut self , index : VnIndex ) -> Option < ConstOperand < ' tcx > > {
884934 // This was already constant in MIR, do not change it.
885- if let Value :: Constant ( const_) = * self . get ( index) {
886- // Some constants may contain pointers. We need to preserve the provenance of these
887- // pointers, but not all constants guarantee this:
888- // - valtrees purposefully do not;
889- // - ConstValue::Slice does not either.
890- let const_ok = match const_ {
891- Const :: Ty ( c) => match c. kind ( ) {
892- ty:: ConstKind :: Value ( valtree) => match valtree {
893- // This is just an integer, keep it.
894- ty:: ValTree :: Leaf ( _) => true ,
895- ty:: ValTree :: Branch ( _) => false ,
896- } ,
897- ty:: ConstKind :: Param ( ..)
898- | ty:: ConstKind :: Unevaluated ( ..)
899- | ty:: ConstKind :: Expr ( ..) => true ,
900- // Should not appear in runtime MIR.
901- ty:: ConstKind :: Infer ( ..)
902- | ty:: ConstKind :: Bound ( ..)
903- | ty:: ConstKind :: Placeholder ( ..)
904- | ty:: ConstKind :: Error ( ..) => bug ! ( ) ,
905- } ,
906- Const :: Unevaluated ( ..) => true ,
907- // If the same slice appears twice in the MIR, we cannot guarantee that we will
908- // give the same `AllocId` to the data.
909- Const :: Val ( ConstValue :: Slice { .. } , _) => false ,
910- Const :: Val (
911- ConstValue :: ZeroSized | ConstValue :: Scalar ( _) | ConstValue :: Indirect { .. } ,
912- _,
913- ) => true ,
914- } ;
915- if const_ok {
916- return Some ( ConstOperand { span : rustc_span:: DUMMY_SP , user_ty : None , const_ } ) ;
917- }
935+ if let Value :: Constant { value, disambiguator : _ } = * self . get ( index)
936+ // If the constant is not deterministic, adding an additional mention of it in MIR will
937+ // not give the same value as the former mention.
938+ && value. is_deterministic ( )
939+ {
940+ return Some ( ConstOperand { span : rustc_span:: DUMMY_SP , user_ty : None , const_ : value } ) ;
918941 }
919942
920943 let op = self . evaluated [ index] . as_ref ( ) ?;
0 commit comments