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