52
52
//! _a = *_b // _b is &Freeze
53
53
//! _c = *_b // replaced by _c = _a
54
54
//! ```
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.
55
84
56
85
use rustc_const_eval:: interpret:: { intern_const_alloc_for_constprop, MemoryKind } ;
57
86
use rustc_const_eval:: interpret:: { ImmTy , InterpCx , OpTy , Projectable , Scalar } ;
@@ -161,7 +190,12 @@ enum Value<'tcx> {
161
190
/// The `usize` is a counter incremented by `new_opaque`.
162
191
Opaque ( usize ) ,
163
192
/// 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
+ } ,
165
199
/// An aggregate value, either tuple/closure/struct/enum.
166
200
/// This does not contain unions, as we cannot reason with the value.
167
201
Aggregate ( AggregateTy < ' tcx > , VariantIdx , Vec < VnIndex > ) ,
@@ -288,8 +322,24 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
288
322
}
289
323
}
290
324
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
+
291
340
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" )
293
343
}
294
344
295
345
#[ instrument( level = "trace" , skip( self ) , ret) ]
@@ -300,7 +350,9 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
300
350
// Do not bother evaluating repeat expressions. This would uselessly consume memory.
301
351
Repeat ( ..) => return None ,
302
352
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
+ }
304
356
Aggregate ( kind, variant, ref fields) => {
305
357
let fields = fields
306
358
. iter ( )
@@ -657,7 +709,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
657
709
match * operand {
658
710
Operand :: Constant ( ref mut constant) => {
659
711
let const_ = constant. const_ . normalize ( self . tcx , self . param_env ) ;
660
- Some ( self . insert ( Value :: Constant ( const_) ) )
712
+ self . insert_constant ( const_)
661
713
}
662
714
Operand :: Copy ( ref mut place) | Operand :: Move ( ref mut place) => {
663
715
let value = self . simplify_place_value ( place, location) ?;
@@ -691,7 +743,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
691
743
Value :: Repeat ( op, amount)
692
744
}
693
745
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) ,
695
747
Rvalue :: Ref ( _, borrow_kind, ref mut place) => {
696
748
self . simplify_place_projection ( place, location) ;
697
749
return self . new_pointer ( * place, AddressKind :: Ref ( borrow_kind) ) ;
@@ -757,7 +809,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
757
809
& mut self ,
758
810
rvalue : & mut Rvalue < ' tcx > ,
759
811
location : Location ,
760
- ) -> Option < Value < ' tcx > > {
812
+ ) -> Option < VnIndex > {
761
813
let Rvalue :: Aggregate ( box ref kind, ref mut fields) = * rvalue else { bug ! ( ) } ;
762
814
763
815
let tcx = self . tcx ;
@@ -774,8 +826,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
774
826
775
827
if is_zst {
776
828
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) ) ;
779
830
}
780
831
}
781
832
@@ -814,12 +865,11 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
814
865
* rvalue = Rvalue :: Repeat ( Operand :: Copy ( local. into ( ) ) , len) ;
815
866
self . reused_locals . insert ( local) ;
816
867
}
817
- return Some ( Value :: Repeat ( first, len) ) ;
868
+ return Some ( self . insert ( Value :: Repeat ( first, len) ) ) ;
818
869
}
819
870
}
820
871
821
- let value = Value :: Aggregate ( ty, variant_index, fields) ;
822
- Some ( value)
872
+ Some ( self . insert ( Value :: Aggregate ( ty, variant_index, fields) ) )
823
873
}
824
874
}
825
875
@@ -882,39 +932,12 @@ impl<'tcx> VnState<'_, 'tcx> {
882
932
/// If `index` is a `Value::Constant`, return the `Constant` to be put in the MIR.
883
933
fn try_as_constant ( & mut self , index : VnIndex ) -> Option < ConstOperand < ' tcx > > {
884
934
// 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 } ) ;
918
941
}
919
942
920
943
let op = self . evaluated [ index] . as_ref ( ) ?;
0 commit comments