Skip to content

Commit 92f9217

Browse files
committed
Auto merge of #3280 - rust-lang:rustup-2024-01-26, r=RalfJung
Automatic Rustup
2 parents 580f0b7 + 57043f2 commit 92f9217

File tree

5 files changed

+118
-3
lines changed

5 files changed

+118
-3
lines changed

rust-version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
f6ee4bf3847277d6d6e2007ff664f8ea0895b11b
1+
dd2559e08e1530806740931037d6bb83ef956161

src/machine.rs

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33
44
use std::borrow::Cow;
55
use std::cell::{Cell, RefCell};
6+
use std::collections::hash_map::Entry;
67
use std::fmt;
78
use std::path::Path;
89
use std::process;
910

1011
use either::Either;
1112
use rand::rngs::StdRng;
13+
use rand::Rng;
1214
use rand::SeedableRng;
1315

1416
use 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`)
4648
pub 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
4956
pub 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

7083
impl<'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

8195
impl 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

557577
impl<'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
}

src/shims/intrinsics/mod.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ use std::iter;
55

66
use log::trace;
77

8+
use rand::Rng;
89
use rustc_apfloat::{Float, Round};
910
use rustc_middle::ty::layout::LayoutOf;
1011
use rustc_middle::{
@@ -141,6 +142,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
141142
this.write_pointer(Pointer::new(ptr.provenance, masked_addr), dest)?;
142143
}
143144

145+
// We want to return either `true` or `false` at random, or else something like
146+
// ```
147+
// if !is_val_statically_known(0) { unreachable_unchecked(); }
148+
// ```
149+
// Would not be considered UB, or the other way around (`is_val_statically_known(0)`).
150+
"is_val_statically_known" => {
151+
let [_] = check_arg_count(args)?;
152+
let branch: bool = this.machine.rng.get_mut().gen();
153+
this.write_scalar(Scalar::from_bool(branch), dest)?;
154+
}
155+
144156
// Floating-point operations
145157
"fabsf32" => {
146158
let [f] = check_arg_count(args)?;

tests/pass/const-addrs.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// The const fn interpreter creates a new AllocId every time it evaluates any const.
2+
// If we do that in Miri, repeatedly evaluating a const causes unbounded memory use
3+
// we need to keep track of the base address for that AllocId, and the allocation is never
4+
// deallocated.
5+
// In Miri we explicitly store previously-assigned AllocIds for each const and ensure
6+
// that we only hand out a finite number of AllocIds per const.
7+
// MIR inlining will put every evaluation of the const we're repeatedly evaluting into the same
8+
// stack frame, breaking this test.
9+
//@compile-flags: -Zinline-mir=no
10+
#![feature(strict_provenance)]
11+
12+
const EVALS: usize = 256;
13+
14+
use std::collections::HashSet;
15+
fn main() {
16+
let mut addrs = HashSet::new();
17+
for _ in 0..EVALS {
18+
addrs.insert(const_addr());
19+
}
20+
// Check that the const allocation has multiple base addresses
21+
assert!(addrs.len() > 1);
22+
// But also that we get a limited number of unique base addresses
23+
assert!(addrs.len() < EVALS);
24+
25+
// Check that within a call we always produce the same address
26+
let mut prev = 0;
27+
for iter in 0..EVALS {
28+
let addr = "test".as_bytes().as_ptr().addr();
29+
if iter > 0 {
30+
assert_eq!(prev, addr);
31+
}
32+
prev = addr;
33+
}
34+
}
35+
36+
fn const_addr() -> usize {
37+
"test".as_bytes().as_ptr().addr()
38+
}

tests/pass/intrinsics.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,21 @@ fn main() {
3333
assert_eq!(intrinsics::likely(false), false);
3434
assert_eq!(intrinsics::unlikely(true), true);
3535

36+
let mut saw_true = false;
37+
let mut saw_false = false;
38+
39+
for _ in 0..50 {
40+
if unsafe { intrinsics::is_val_statically_known(0) } {
41+
saw_true = true;
42+
} else {
43+
saw_false = true;
44+
}
45+
}
46+
assert!(
47+
saw_true && saw_false,
48+
"`is_val_statically_known` failed to return both true and false. Congrats, you won the lottery!"
49+
);
50+
3651
intrinsics::forget(Bomb);
3752

3853
let _v = intrinsics::discriminant_value(&Some(()));

0 commit comments

Comments
 (0)