Skip to content

Commit c6b55ee

Browse files
committed
Auto merge of #70598 - vakaras:add-threads-cr3, r=oli-obk,RalfJung
Make the necessary changes to support concurrency in Miri. This pull request makes the necessary changes to the Rust compiler to allow Miri to support concurrency: 1. Move stack from the interpretation context (`InterpCx`) to machine, so that the machine can switch the stacks when it changes the thread being executed. 2. Add the callbacks that allow the machine to generate fresh allocation ids for each thread local allocation and to translate them back to original allocations when needed. This allows the machine to ensure the property that allocation ids are unique, which allows using a simpler representation of the memory. r? @oli-obk cc @RalfJung
2 parents dbf8b6b + cbd9222 commit c6b55ee

17 files changed

+172
-70
lines changed

src/librustc_mir/const_eval/error.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ impl Error for ConstEvalErrKind {}
5050
/// Turn an interpreter error into something to report to the user.
5151
/// As a side-effect, if RUSTC_CTFE_BACKTRACE is set, this prints the backtrace.
5252
/// Should be called only if the error is actually going to to be reported!
53-
pub fn error_to_const_error<'mir, 'tcx, M: Machine<'mir, 'tcx>>(
53+
pub fn error_to_const_error<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>>(
5454
ecx: &InterpCx<'mir, 'tcx, M>,
5555
mut error: InterpErrorInfo<'tcx>,
5656
) -> ConstEvalErr<'tcx> {

src/librustc_mir/const_eval/machine.rs

+24-6
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use crate::interpret::{
1919

2020
use super::error::*;
2121

22-
impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter> {
22+
impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> {
2323
/// Evaluate a const function where all arguments (if any) are zero-sized types.
2424
/// The evaluation is memoized thanks to the query system.
2525
///
@@ -86,12 +86,15 @@ impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter> {
8686
}
8787

8888
/// Extra machine state for CTFE, and the Machine instance
89-
pub struct CompileTimeInterpreter {
89+
pub struct CompileTimeInterpreter<'mir, 'tcx> {
9090
/// For now, the number of terminators that can be evaluated before we throw a resource
9191
/// exhuastion error.
9292
///
9393
/// Setting this to `0` disables the limit and allows the interpreter to run forever.
9494
pub steps_remaining: usize,
95+
96+
/// The virtual call stack.
97+
pub(crate) stack: Vec<Frame<'mir, 'tcx, (), ()>>,
9598
}
9699

97100
#[derive(Copy, Clone, Debug)]
@@ -100,9 +103,9 @@ pub struct MemoryExtra {
100103
pub(super) can_access_statics: bool,
101104
}
102105

103-
impl CompileTimeInterpreter {
106+
impl<'mir, 'tcx> CompileTimeInterpreter<'mir, 'tcx> {
104107
pub(super) fn new(const_eval_limit: usize) -> Self {
105-
CompileTimeInterpreter { steps_remaining: const_eval_limit }
108+
CompileTimeInterpreter { steps_remaining: const_eval_limit, stack: Vec::new() }
106109
}
107110
}
108111

@@ -156,7 +159,8 @@ impl<K: Hash + Eq, V> interpret::AllocMap<K, V> for FxHashMap<K, V> {
156159
}
157160
}
158161

159-
crate type CompileTimeEvalContext<'mir, 'tcx> = InterpCx<'mir, 'tcx, CompileTimeInterpreter>;
162+
crate type CompileTimeEvalContext<'mir, 'tcx> =
163+
InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>>;
160164

161165
impl interpret::MayLeak for ! {
162166
#[inline(always)]
@@ -166,7 +170,7 @@ impl interpret::MayLeak for ! {
166170
}
167171
}
168172

169-
impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter {
173+
impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir, 'tcx> {
170174
type MemoryKind = !;
171175
type PointerTag = ();
172176
type ExtraFnVal = !;
@@ -349,6 +353,20 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter {
349353
Ok(frame)
350354
}
351355

356+
#[inline(always)]
357+
fn stack(
358+
ecx: &'a InterpCx<'mir, 'tcx, Self>,
359+
) -> &'a [Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>] {
360+
&ecx.machine.stack
361+
}
362+
363+
#[inline(always)]
364+
fn stack_mut(
365+
ecx: &'a mut InterpCx<'mir, 'tcx, Self>,
366+
) -> &'a mut Vec<Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>> {
367+
&mut ecx.machine.stack
368+
}
369+
352370
fn before_access_global(
353371
memory_extra: &MemoryExtra,
354372
alloc_id: AllocId,

src/librustc_mir/interpret/cast.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rustc_middle::ty::{self, Ty, TypeAndMut, TypeFoldable};
1212
use rustc_span::symbol::sym;
1313
use rustc_target::abi::{LayoutOf, Size, Variants};
1414

15-
impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
15+
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
1616
pub fn cast(
1717
&mut self,
1818
src: OpTy<'tcx, M::PointerTag>,

src/librustc_mir/interpret/eval_context.rs

+32-25
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ use crate::util::storage::AlwaysLiveLocals;
2828

2929
pub struct InterpCx<'mir, 'tcx, M: Machine<'mir, 'tcx>> {
3030
/// Stores the `Machine` instance.
31+
///
32+
/// Note: the stack is provided by the machine.
3133
pub machine: M,
3234

3335
/// The results of the type checker, from rustc.
@@ -39,9 +41,6 @@ pub struct InterpCx<'mir, 'tcx, M: Machine<'mir, 'tcx>> {
3941
/// The virtual memory system.
4042
pub memory: Memory<'mir, 'tcx, M>,
4143

42-
/// The virtual call stack.
43-
pub(crate) stack: Vec<Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>>,
44-
4544
/// A cache for deduplicating vtables
4645
pub(super) vtables:
4746
FxHashMap<(Ty<'tcx>, Option<ty::PolyExistentialTraitRef<'tcx>>), Pointer<M::PointerTag>>,
@@ -297,7 +296,7 @@ pub(super) fn from_known_layout<'tcx>(
297296
}
298297
}
299298

300-
impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
299+
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
301300
pub fn new(
302301
tcx: TyCtxtAt<'tcx>,
303302
param_env: ty::ParamEnv<'tcx>,
@@ -309,7 +308,6 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
309308
tcx,
310309
param_env,
311310
memory: Memory::new(tcx, memory_extra),
312-
stack: Vec::new(),
313311
vtables: FxHashMap::default(),
314312
}
315313
}
@@ -349,24 +347,32 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
349347
}
350348

351349
#[inline(always)]
352-
pub fn stack(&self) -> &[Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>] {
353-
&self.stack
350+
pub(crate) fn stack(&self) -> &[Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>] {
351+
M::stack(self)
352+
}
353+
354+
#[inline(always)]
355+
pub(crate) fn stack_mut(
356+
&mut self,
357+
) -> &mut Vec<Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>> {
358+
M::stack_mut(self)
354359
}
355360

356361
#[inline(always)]
357-
pub fn cur_frame(&self) -> usize {
358-
assert!(!self.stack.is_empty());
359-
self.stack.len() - 1
362+
pub fn frame_idx(&self) -> usize {
363+
let stack = self.stack();
364+
assert!(!stack.is_empty());
365+
stack.len() - 1
360366
}
361367

362368
#[inline(always)]
363369
pub fn frame(&self) -> &Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra> {
364-
self.stack.last().expect("no call frames exist")
370+
self.stack().last().expect("no call frames exist")
365371
}
366372

367373
#[inline(always)]
368374
pub fn frame_mut(&mut self) -> &mut Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra> {
369-
self.stack.last_mut().expect("no call frames exist")
375+
self.stack_mut().last_mut().expect("no call frames exist")
370376
}
371377

372378
#[inline(always)]
@@ -596,8 +602,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
596602
return_place: Option<PlaceTy<'tcx, M::PointerTag>>,
597603
return_to_block: StackPopCleanup,
598604
) -> InterpResult<'tcx> {
599-
if !self.stack.is_empty() {
600-
info!("PAUSING({}) {}", self.cur_frame(), self.frame().instance);
605+
if !self.stack().is_empty() {
606+
info!("PAUSING({}) {}", self.frame_idx(), self.frame().instance);
601607
}
602608
::log_settings::settings().indentation += 1;
603609

@@ -615,7 +621,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
615621
extra: (),
616622
};
617623
let frame = M::init_frame_extra(self, pre_frame)?;
618-
self.stack.push(frame);
624+
self.stack_mut().push(frame);
619625

620626
// don't allocate at all for trivial constants
621627
if body.local_decls.len() > 1 {
@@ -648,9 +654,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
648654
}
649655

650656
M::after_stack_push(self)?;
651-
info!("ENTERING({}) {}", self.cur_frame(), self.frame().instance);
657+
info!("ENTERING({}) {}", self.frame_idx(), self.frame().instance);
652658

653-
if self.stack.len() > *self.tcx.sess.recursion_limit.get() {
659+
if self.stack().len() > *self.tcx.sess.recursion_limit.get() {
654660
throw_exhaust!(StackFrameLimitReached)
655661
} else {
656662
Ok(())
@@ -705,7 +711,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
705711
pub(super) fn pop_stack_frame(&mut self, unwinding: bool) -> InterpResult<'tcx> {
706712
info!(
707713
"LEAVING({}) {} (unwinding = {})",
708-
self.cur_frame(),
714+
self.frame_idx(),
709715
self.frame().instance,
710716
unwinding
711717
);
@@ -720,7 +726,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
720726
);
721727

722728
::log_settings::settings().indentation -= 1;
723-
let frame = self.stack.pop().expect("tried to pop a stack frame, but there were none");
729+
let frame =
730+
self.stack_mut().pop().expect("tried to pop a stack frame, but there were none");
724731

725732
// Now where do we jump next?
726733

@@ -735,7 +742,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
735742
};
736743

737744
if !cleanup {
738-
assert!(self.stack.is_empty(), "only the topmost frame should ever be leaked");
745+
assert!(self.stack().is_empty(), "only the topmost frame should ever be leaked");
739746
assert!(next_block.is_none(), "tried to skip cleanup when we have a next block!");
740747
assert!(!unwinding, "tried to skip cleanup during unwinding");
741748
// Leak the locals, skip validation, skip machine hook.
@@ -784,10 +791,10 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
784791
}
785792
}
786793

787-
if !self.stack.is_empty() {
794+
if !self.stack().is_empty() {
788795
info!(
789796
"CONTINUING({}) {} (unwinding = {})",
790-
self.cur_frame(),
797+
self.frame_idx(),
791798
self.frame().instance,
792799
unwinding
793800
);
@@ -895,12 +902,12 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
895902
Place::Local { frame, local } => {
896903
let mut allocs = Vec::new();
897904
let mut msg = format!("{:?}", local);
898-
if frame != self.cur_frame() {
899-
write!(msg, " ({} frames up)", self.cur_frame() - frame).unwrap();
905+
if frame != self.frame_idx() {
906+
write!(msg, " ({} frames up)", self.frame_idx() - frame).unwrap();
900907
}
901908
write!(msg, ":").unwrap();
902909

903-
match self.stack[frame].locals[local].value {
910+
match self.stack()[frame].locals[local].value {
904911
LocalValue::Dead => write!(msg, " is dead").unwrap(),
905912
LocalValue::Uninitialized => write!(msg, " is uninitialized").unwrap(),
906913
LocalValue::Live(Operand::Indirect(mplace)) => match mplace.ptr {

src/librustc_mir/interpret/intern.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ impl<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>> InternVisitor<'rt, 'mir
149149
}
150150
}
151151

152-
impl<'rt, 'mir, 'tcx, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
152+
impl<'rt, 'mir, 'tcx: 'mir, M: CompileTimeMachine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
153153
for InternVisitor<'rt, 'mir, 'tcx, M>
154154
{
155155
type V = MPlaceTy<'tcx>;
@@ -286,7 +286,10 @@ pub fn intern_const_alloc_recursive<M: CompileTimeMachine<'mir, 'tcx>>(
286286
intern_kind: InternKind,
287287
ret: MPlaceTy<'tcx>,
288288
ignore_interior_mut_in_const_validation: bool,
289-
) -> InterpResult<'tcx> {
289+
) -> InterpResult<'tcx>
290+
where
291+
'tcx: 'mir,
292+
{
290293
let tcx = ecx.tcx;
291294
let (base_mutability, base_intern_mode) = match intern_kind {
292295
// `static mut` doesn't care about interior mutability, it's mutable anyway

src/librustc_mir/interpret/intrinsics.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ crate fn eval_nullary_intrinsic<'tcx>(
7373
})
7474
}
7575

76-
impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
76+
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
7777
/// Returns `true` if emulation happened.
7878
pub fn emulate_intrinsic(
7979
&mut self,

src/librustc_mir/interpret/intrinsics/caller_location.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@ use crate::interpret::{
1010
MPlaceTy, MemoryKind, Scalar,
1111
};
1212

13-
impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
13+
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
1414
/// Walks up the callstack from the intrinsic's callsite, searching for the first callsite in a
1515
/// frame which is not `#[track_caller]`.
1616
crate fn find_closest_untracked_caller_location(&self) -> Span {
17-
self.stack
17+
self.stack()
1818
.iter()
1919
.rev()
2020
// Find first non-`#[track_caller]` frame.

src/librustc_mir/interpret/machine.rs

+38
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,34 @@ pub trait Machine<'mir, 'tcx>: Sized {
230230
id
231231
}
232232

233+
/// Called when converting a `ty::Const` to an operand (in
234+
/// `eval_const_to_op`).
235+
///
236+
/// Miri uses this callback for creating per thread allocations for thread
237+
/// locals. In Rust, one way of creating a thread local is by marking a
238+
/// static with `#[thread_local]`. On supported platforms this gets
239+
/// translated to a LLVM thread local for which LLVM automatically ensures
240+
/// that each thread gets its own copy. Since LLVM automatically handles
241+
/// thread locals, the Rust compiler just treats thread local statics as
242+
/// regular statics even though accessing a thread local static should be an
243+
/// effectful computation that depends on the current thread. The long term
244+
/// plan is to change MIR to make accesses to thread locals explicit
245+
/// (https://github.com/rust-lang/rust/issues/70685). While the issue 70685
246+
/// is not fixed, our current workaround in Miri is to use this function to
247+
/// make per-thread copies of thread locals. Please note that we cannot make
248+
/// these copies in `canonical_alloc_id` because that is too late: for
249+
/// example, if one created a pointer in thread `t1` to a thread local and
250+
/// sent it to another thread `t2`, resolving the access in
251+
/// `canonical_alloc_id` would result in pointer pointing to `t2`'s thread
252+
/// local and not `t1` as it should.
253+
#[inline]
254+
fn adjust_global_const(
255+
_ecx: &InterpCx<'mir, 'tcx, Self>,
256+
val: mir::interpret::ConstValue<'tcx>,
257+
) -> InterpResult<'tcx, mir::interpret::ConstValue<'tcx>> {
258+
Ok(val)
259+
}
260+
233261
/// Called to initialize the "extra" state of an allocation and make the pointers
234262
/// it contains (in relocations) tagged. The way we construct allocations is
235263
/// to always first construct it without extra and then add the extra.
@@ -285,6 +313,16 @@ pub trait Machine<'mir, 'tcx>: Sized {
285313
frame: Frame<'mir, 'tcx, Self::PointerTag>,
286314
) -> InterpResult<'tcx, Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>>;
287315

316+
/// Borrow the current thread's stack.
317+
fn stack(
318+
ecx: &'a InterpCx<'mir, 'tcx, Self>,
319+
) -> &'a [Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>];
320+
321+
/// Mutably borrow the current thread's stack.
322+
fn stack_mut(
323+
ecx: &'a mut InterpCx<'mir, 'tcx, Self>,
324+
) -> &'a mut Vec<Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>>;
325+
288326
/// Called immediately after a stack frame got pushed and its locals got initialized.
289327
fn after_stack_push(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
290328
Ok(())

src/librustc_mir/interpret/operand.rs

+12-2
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ impl<'tcx, Tag: Copy> ImmTy<'tcx, Tag> {
209209
}
210210
}
211211

212-
impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
212+
impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
213213
/// Normalice `place.ptr` to a `Pointer` if this is a place and not a ZST.
214214
/// Can be helpful to avoid lots of `force_ptr` calls later, if this place is used a lot.
215215
#[inline]
@@ -440,7 +440,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
440440
) -> InterpResult<'tcx, OpTy<'tcx, M::PointerTag>> {
441441
let op = match *place {
442442
Place::Ptr(mplace) => Operand::Indirect(mplace),
443-
Place::Local { frame, local } => *self.access_local(&self.stack[frame], local, None)?,
443+
Place::Local { frame, local } => {
444+
*self.access_local(&self.stack()[frame], local, None)?
445+
}
444446
};
445447
Ok(OpTy { op, layout: place.layout })
446448
}
@@ -528,6 +530,10 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
528530
// potentially requiring the current static to be evaluated again. This is not a
529531
// problem here, because we are building an operand which means an actual read is
530532
// happening.
533+
//
534+
// The machine callback `adjust_global_const` below is guaranteed to
535+
// be called for all constants because `const_eval` calls
536+
// `eval_const_to_op` recursively.
531537
return Ok(self.const_eval(GlobalId { instance, promoted }, val.ty)?);
532538
}
533539
ty::ConstKind::Infer(..)
@@ -537,6 +543,10 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
537543
}
538544
ty::ConstKind::Value(val_val) => val_val,
539545
};
546+
// This call allows the machine to create fresh allocation ids for
547+
// thread-local statics (see the `adjust_global_const` function
548+
// documentation).
549+
let val_val = M::adjust_global_const(self, val_val)?;
540550
// Other cases need layout.
541551
let layout = from_known_layout(self.tcx, layout, || self.layout_of(val.ty))?;
542552
let op = match val_val {

0 commit comments

Comments
 (0)