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 ;
13+ use rand:: Rng ;
1214use rand:: SeedableRng ;
1315
1416use rustc_ast:: ast:: Mutability ;
@@ -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,19 @@ 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 : _ } =
87+ self ;
7488 f. debug_struct ( "FrameData" )
7589 . field ( "borrow_tracker" , borrow_tracker)
7690 . field ( "catch_unwind" , catch_unwind)
@@ -80,7 +94,8 @@ impl<'tcx> std::fmt::Debug for FrameExtra<'tcx> {
8094
8195impl VisitProvenance for FrameExtra < ' _ > {
8296 fn visit_provenance ( & self , visit : & mut VisitWith < ' _ > ) {
83- let FrameExtra { catch_unwind, borrow_tracker, timing : _, is_user_relevant : _ } = self ;
97+ let FrameExtra { catch_unwind, borrow_tracker, timing : _, is_user_relevant : _, salt : _ } =
98+ self ;
8499
85100 catch_unwind. visit_provenance ( visit) ;
86101 borrow_tracker. visit_provenance ( visit) ;
@@ -552,6 +567,11 @@ pub struct MiriMachine<'mir, 'tcx> {
552567 /// The spans we will use to report where an allocation was created and deallocated in
553568 /// diagnostics.
554569 pub ( crate ) allocation_spans : RefCell < FxHashMap < AllocId , ( Span , Option < Span > ) > > ,
570+
571+ /// Maps MIR consts to their evaluated result. We combine the const with a "salt" (`usize`)
572+ /// that is fixed per stack frame; this lets us have sometimes different results for the
573+ /// same const while ensuring consistent results within a single call.
574+ const_cache : RefCell < FxHashMap < ( mir:: Const < ' tcx > , usize ) , OpTy < ' tcx , Provenance > > > ,
555575}
556576
557577impl < ' mir , ' tcx > MiriMachine < ' mir , ' tcx > {
@@ -677,6 +697,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
677697 stack_size,
678698 collect_leak_backtraces : config. collect_leak_backtraces ,
679699 allocation_spans : RefCell :: new ( FxHashMap :: default ( ) ) ,
700+ const_cache : RefCell :: new ( FxHashMap :: default ( ) ) ,
680701 }
681702 }
682703
@@ -788,6 +809,7 @@ impl VisitProvenance for MiriMachine<'_, '_> {
788809 stack_size : _,
789810 collect_leak_backtraces : _,
790811 allocation_spans : _,
812+ const_cache : _,
791813 } = self ;
792814
793815 threads. visit_provenance ( visit) ;
@@ -1345,6 +1367,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
13451367 catch_unwind : None ,
13461368 timing,
13471369 is_user_relevant : ecx. machine . is_user_relevant ( & frame) ,
1370+ salt : ecx. machine . rng . borrow_mut ( ) . gen :: < usize > ( ) % ADDRS_PER_CONST ,
13481371 } ;
13491372
13501373 Ok ( frame. with_extra ( extra) )
@@ -1450,4 +1473,31 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
14501473 ecx. machine . allocation_spans . borrow_mut ( ) . insert ( alloc_id, ( span, None ) ) ;
14511474 Ok ( ( ) )
14521475 }
1476+
1477+ fn eval_mir_constant < F > (
1478+ ecx : & InterpCx < ' mir , ' tcx , Self > ,
1479+ val : mir:: Const < ' tcx > ,
1480+ span : Option < Span > ,
1481+ layout : Option < TyAndLayout < ' tcx > > ,
1482+ eval : F ,
1483+ ) -> InterpResult < ' tcx , OpTy < ' tcx , Self :: Provenance > >
1484+ where
1485+ F : Fn (
1486+ & InterpCx < ' mir , ' tcx , Self > ,
1487+ mir:: Const < ' tcx > ,
1488+ Option < Span > ,
1489+ Option < TyAndLayout < ' tcx > > ,
1490+ ) -> InterpResult < ' tcx , OpTy < ' tcx , Self :: Provenance > > ,
1491+ {
1492+ let frame = ecx. active_thread_stack ( ) . last ( ) . unwrap ( ) ;
1493+ let mut cache = ecx. machine . const_cache . borrow_mut ( ) ;
1494+ match cache. entry ( ( val, frame. extra . salt ) ) {
1495+ Entry :: Vacant ( ve) => {
1496+ let op = eval ( ecx, val, span, layout) ?;
1497+ ve. insert ( op. clone ( ) ) ;
1498+ Ok ( op)
1499+ }
1500+ Entry :: Occupied ( oe) => Ok ( oe. get ( ) . clone ( ) ) ,
1501+ }
1502+ }
14531503}
0 commit comments