3
3
4
4
use std:: borrow:: Cow ;
5
5
use std:: cell:: { Cell , RefCell } ;
6
+ use std:: collections:: hash_map:: Entry ;
6
7
use std:: fmt;
7
8
use std:: path:: Path ;
8
9
use std:: process;
9
10
10
11
use either:: Either ;
11
12
use rand:: rngs:: StdRng ;
12
13
use rand:: SeedableRng ;
14
+ use rand:: Rng ;
13
15
14
16
use rustc_ast:: ast:: Mutability ;
15
17
use rustc_data_structures:: fx:: { FxHashMap , FxHashSet } ;
@@ -45,6 +47,11 @@ pub const SIGRTMIN: i32 = 34;
45
47
/// `SIGRTMAX` - `SIGRTMIN` >= 8 (which is the value of `_POSIX_RTSIG_MAX`)
46
48
pub const SIGRTMAX : i32 = 42 ;
47
49
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
+
48
55
/// Extra data stored with each stack frame
49
56
pub struct FrameExtra < ' tcx > {
50
57
/// Extra data for the Borrow Tracker.
@@ -65,12 +72,18 @@ pub struct FrameExtra<'tcx> {
65
72
/// optimization.
66
73
/// This is used by `MiriMachine::current_span` and `MiriMachine::caller_span`
67
74
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 ,
68
81
}
69
82
70
83
impl < ' tcx > std:: fmt:: Debug for FrameExtra < ' tcx > {
71
84
fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
72
85
// 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 ;
74
87
f. debug_struct ( "FrameData" )
75
88
. field ( "borrow_tracker" , borrow_tracker)
76
89
. field ( "catch_unwind" , catch_unwind)
@@ -80,7 +93,7 @@ impl<'tcx> std::fmt::Debug for FrameExtra<'tcx> {
80
93
81
94
impl VisitProvenance for FrameExtra < ' _ > {
82
95
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 ;
84
97
85
98
catch_unwind. visit_provenance ( visit) ;
86
99
borrow_tracker. visit_provenance ( visit) ;
@@ -552,6 +565,11 @@ pub struct MiriMachine<'mir, 'tcx> {
552
565
/// The spans we will use to report where an allocation was created and deallocated in
553
566
/// diagnostics.
554
567
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 > > > ,
555
573
}
556
574
557
575
impl < ' mir , ' tcx > MiriMachine < ' mir , ' tcx > {
@@ -677,6 +695,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
677
695
stack_size,
678
696
collect_leak_backtraces : config. collect_leak_backtraces ,
679
697
allocation_spans : RefCell :: new ( FxHashMap :: default ( ) ) ,
698
+ const_cache : RefCell :: new ( FxHashMap :: default ( ) ) ,
680
699
}
681
700
}
682
701
@@ -857,6 +876,7 @@ impl VisitProvenance for MiriMachine<'_, '_> {
857
876
stack_size : _,
858
877
collect_leak_backtraces : _,
859
878
allocation_spans : _,
879
+ const_cache : _,
860
880
} = self ;
861
881
862
882
threads. visit_provenance ( visit) ;
@@ -1400,6 +1420,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
1400
1420
catch_unwind : None ,
1401
1421
timing,
1402
1422
is_user_relevant : ecx. machine . is_user_relevant ( & frame) ,
1423
+ salt : ecx. machine . rng . borrow_mut ( ) . gen :: < usize > ( ) % ADDRS_PER_CONST ,
1403
1424
} ;
1404
1425
1405
1426
Ok ( frame. with_extra ( extra) )
@@ -1505,4 +1526,31 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
1505
1526
ecx. machine . allocation_spans . borrow_mut ( ) . insert ( alloc_id, ( span, None ) ) ;
1506
1527
Ok ( ( ) )
1507
1528
}
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
+ }
1508
1556
}
0 commit comments