33
44use std:: borrow:: Cow ;
55use std:: cell:: { Cell , RefCell } ;
6+ use std:: collections:: hash_map:: Entry ;
67use std:: fmt;
78use std:: path:: Path ;
89use std:: process;
910
1011use either:: Either ;
1112use rand:: rngs:: StdRng ;
1213use rand:: SeedableRng ;
14+ use rand:: Rng ;
1315
1416use rustc_ast:: ast:: Mutability ;
1517use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
@@ -45,6 +47,11 @@ pub const SIGRTMIN: i32 = 34;
4547/// `SIGRTMAX` - `SIGRTMIN` >= 8 (which is the value of `_POSIX_RTSIG_MAX`)
4648pub const SIGRTMAX : i32 = 42 ;
4749
50+ /// Each const has multiple addresses, but only this many. Since const allocations are never
51+ /// deallocated, choosing a new [`AllocId`] and thus base address for each evaluation would
52+ /// produce unbounded memory usage.
53+ const ADDRS_PER_CONST : usize = 16 ;
54+
4855/// Extra data stored with each stack frame
4956pub struct FrameExtra < ' tcx > {
5057 /// Extra data for the Borrow Tracker.
@@ -65,12 +72,18 @@ pub struct FrameExtra<'tcx> {
6572 /// optimization.
6673 /// This is used by `MiriMachine::current_span` and `MiriMachine::caller_span`
6774 pub is_user_relevant : bool ,
75+
76+ /// We have a cache for the mapping from [`mir::Const`] to resulting [`AllocId`].
77+ /// However, we don't want all frames to always get the same result, so we insert
78+ /// an additional bit of "salt" into the cache key. This salt is fixed per-frame
79+ /// so that within a call, a const will have a stable address.
80+ salt : usize ,
6881}
6982
7083impl < ' tcx > std:: fmt:: Debug for FrameExtra < ' tcx > {
7184 fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
7285 // Omitting `timing`, it does not support `Debug`.
73- let FrameExtra { borrow_tracker, catch_unwind, timing : _, is_user_relevant : _ } = self ;
86+ let FrameExtra { borrow_tracker, catch_unwind, timing : _, is_user_relevant : _, salt : _ } = self ;
7487 f. debug_struct ( "FrameData" )
7588 . field ( "borrow_tracker" , borrow_tracker)
7689 . field ( "catch_unwind" , catch_unwind)
@@ -80,7 +93,7 @@ impl<'tcx> std::fmt::Debug for FrameExtra<'tcx> {
8093
8194impl VisitProvenance for FrameExtra < ' _ > {
8295 fn visit_provenance ( & self , visit : & mut VisitWith < ' _ > ) {
83- let FrameExtra { catch_unwind, borrow_tracker, timing : _, is_user_relevant : _ } = self ;
96+ let FrameExtra { catch_unwind, borrow_tracker, timing : _, is_user_relevant : _, salt : _ } = self ;
8497
8598 catch_unwind. visit_provenance ( visit) ;
8699 borrow_tracker. visit_provenance ( visit) ;
@@ -552,6 +565,11 @@ pub struct MiriMachine<'mir, 'tcx> {
552565 /// The spans we will use to report where an allocation was created and deallocated in
553566 /// diagnostics.
554567 pub ( crate ) allocation_spans : RefCell < FxHashMap < AllocId , ( Span , Option < Span > ) > > ,
568+
569+ /// Maps MIR consts to their evaluated result. We combine the const with a "salt" (`usize`)
570+ /// that is fixed per stack frame; this lets us have sometimes different results for the
571+ /// same const while ensuring consistent results within a single call.
572+ const_cache : RefCell < FxHashMap < ( mir:: Const < ' tcx > , usize ) , OpTy < ' tcx , Provenance > > > ,
555573}
556574
557575impl < ' mir , ' tcx > MiriMachine < ' mir , ' tcx > {
@@ -677,6 +695,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
677695 stack_size,
678696 collect_leak_backtraces : config. collect_leak_backtraces ,
679697 allocation_spans : RefCell :: new ( FxHashMap :: default ( ) ) ,
698+ const_cache : RefCell :: new ( FxHashMap :: default ( ) ) ,
680699 }
681700 }
682701
@@ -857,6 +876,7 @@ impl VisitProvenance for MiriMachine<'_, '_> {
857876 stack_size : _,
858877 collect_leak_backtraces : _,
859878 allocation_spans : _,
879+ const_cache : _,
860880 } = self ;
861881
862882 threads. visit_provenance ( visit) ;
@@ -1400,6 +1420,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
14001420 catch_unwind : None ,
14011421 timing,
14021422 is_user_relevant : ecx. machine . is_user_relevant ( & frame) ,
1423+ salt : ecx. machine . rng . borrow_mut ( ) . gen :: < usize > ( ) % ADDRS_PER_CONST ,
14031424 } ;
14041425
14051426 Ok ( frame. with_extra ( extra) )
@@ -1505,4 +1526,31 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
15051526 ecx. machine . allocation_spans . borrow_mut ( ) . insert ( alloc_id, ( span, None ) ) ;
15061527 Ok ( ( ) )
15071528 }
1529+
1530+ fn eval_mir_constant < F > (
1531+ ecx : & InterpCx < ' mir , ' tcx , Self > ,
1532+ val : mir:: Const < ' tcx > ,
1533+ span : Option < Span > ,
1534+ layout : Option < TyAndLayout < ' tcx > > ,
1535+ eval : F ,
1536+ ) -> InterpResult < ' tcx , OpTy < ' tcx , Self :: Provenance > >
1537+ where
1538+ F : Fn (
1539+ & InterpCx < ' mir , ' tcx , Self > ,
1540+ mir:: Const < ' tcx > ,
1541+ Option < Span > ,
1542+ Option < TyAndLayout < ' tcx > > ,
1543+ ) -> InterpResult < ' tcx , OpTy < ' tcx , Self :: Provenance > > ,
1544+ {
1545+ let frame = ecx. active_thread_stack ( ) . last ( ) . unwrap ( ) ;
1546+ let mut cache = ecx. machine . const_cache . borrow_mut ( ) ;
1547+ match cache. entry ( ( val, frame. extra . salt ) ) {
1548+ Entry :: Vacant ( ve) => {
1549+ let op = eval ( ecx, val, span, layout) ?;
1550+ ve. insert ( op. clone ( ) ) ;
1551+ Ok ( op)
1552+ }
1553+ Entry :: Occupied ( oe) => Ok ( oe. get ( ) . clone ( ) ) ,
1554+ }
1555+ }
15081556}
0 commit comments