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 ;
13
+ use rand:: Rng ;
12
14
use rand:: SeedableRng ;
13
15
14
16
use rustc_ast:: ast:: Mutability ;
@@ -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,19 @@ 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 : _ } =
87
+ self ;
74
88
f. debug_struct ( "FrameData" )
75
89
. field ( "borrow_tracker" , borrow_tracker)
76
90
. field ( "catch_unwind" , catch_unwind)
@@ -80,7 +94,8 @@ impl<'tcx> std::fmt::Debug for FrameExtra<'tcx> {
80
94
81
95
impl VisitProvenance for FrameExtra < ' _ > {
82
96
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 ;
84
99
85
100
catch_unwind. visit_provenance ( visit) ;
86
101
borrow_tracker. visit_provenance ( visit) ;
@@ -552,6 +567,11 @@ pub struct MiriMachine<'mir, 'tcx> {
552
567
/// The spans we will use to report where an allocation was created and deallocated in
553
568
/// diagnostics.
554
569
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 > > > ,
555
575
}
556
576
557
577
impl < ' mir , ' tcx > MiriMachine < ' mir , ' tcx > {
@@ -677,6 +697,7 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
677
697
stack_size,
678
698
collect_leak_backtraces : config. collect_leak_backtraces ,
679
699
allocation_spans : RefCell :: new ( FxHashMap :: default ( ) ) ,
700
+ const_cache : RefCell :: new ( FxHashMap :: default ( ) ) ,
680
701
}
681
702
}
682
703
@@ -788,6 +809,7 @@ impl VisitProvenance for MiriMachine<'_, '_> {
788
809
stack_size : _,
789
810
collect_leak_backtraces : _,
790
811
allocation_spans : _,
812
+ const_cache : _,
791
813
} = self ;
792
814
793
815
threads. visit_provenance ( visit) ;
@@ -1345,6 +1367,7 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
1345
1367
catch_unwind : None ,
1346
1368
timing,
1347
1369
is_user_relevant : ecx. machine . is_user_relevant ( & frame) ,
1370
+ salt : ecx. machine . rng . borrow_mut ( ) . gen :: < usize > ( ) % ADDRS_PER_CONST ,
1348
1371
} ;
1349
1372
1350
1373
Ok ( frame. with_extra ( extra) )
@@ -1450,4 +1473,31 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for MiriMachine<'mir, 'tcx> {
1450
1473
ecx. machine . allocation_spans . borrow_mut ( ) . insert ( alloc_id, ( span, None ) ) ;
1451
1474
Ok ( ( ) )
1452
1475
}
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
+ }
1453
1503
}
0 commit comments