Skip to content

Commit 9e46807

Browse files
author
Vytautas Astrauskas
committed
Add function eval_maybe_thread_local_static_const that allows handling thread locals without touching debug info; address other PR comments.
1 parent 2960a6c commit 9e46807

File tree

7 files changed

+70
-52
lines changed

7 files changed

+70
-52
lines changed

src/librustc_mir/interpret/eval_context.rs

+13-9
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.
@@ -343,17 +345,19 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
343345
}
344346

345347
#[inline(always)]
346-
pub fn stack(&self) -> &[Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>] {
348+
pub(crate) fn stack(&self) -> &[Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>] {
347349
M::stack(self)
348350
}
349351

350352
#[inline(always)]
351-
pub fn stack_mut(&mut self) -> &mut Vec<Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>> {
353+
pub(crate) fn stack_mut(
354+
&mut self,
355+
) -> &mut Vec<Frame<'mir, 'tcx, M::PointerTag, M::FrameExtra>> {
352356
M::stack_mut(self)
353357
}
354358

355359
#[inline(always)]
356-
pub fn cur_frame(&self) -> usize {
360+
pub fn frame_idx(&self) -> usize {
357361
let stack = self.stack();
358362
assert!(!stack.is_empty());
359363
stack.len() - 1
@@ -598,7 +602,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
598602
return_to_block: StackPopCleanup,
599603
) -> InterpResult<'tcx> {
600604
if !self.stack().is_empty() {
601-
info!("PAUSING({}) {}", self.cur_frame(), self.frame().instance);
605+
info!("PAUSING({}) {}", self.frame_idx(), self.frame().instance);
602606
}
603607
::log_settings::settings().indentation += 1;
604608

@@ -649,7 +653,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
649653
}
650654

651655
M::after_stack_push(self)?;
652-
info!("ENTERING({}) {}", self.cur_frame(), self.frame().instance);
656+
info!("ENTERING({}) {}", self.frame_idx(), self.frame().instance);
653657

654658
if self.stack().len() > *self.tcx.sess.recursion_limit.get() {
655659
throw_exhaust!(StackFrameLimitReached)
@@ -706,7 +710,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
706710
pub(super) fn pop_stack_frame(&mut self, unwinding: bool) -> InterpResult<'tcx> {
707711
info!(
708712
"LEAVING({}) {} (unwinding = {})",
709-
self.cur_frame(),
713+
self.frame_idx(),
710714
self.frame().instance,
711715
unwinding
712716
);
@@ -789,7 +793,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
789793
if !self.stack().is_empty() {
790794
info!(
791795
"CONTINUING({}) {} (unwinding = {})",
792-
self.cur_frame(),
796+
self.frame_idx(),
793797
self.frame().instance,
794798
unwinding
795799
);
@@ -897,8 +901,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
897901
Place::Local { frame, local } => {
898902
let mut allocs = Vec::new();
899903
let mut msg = format!("{:?}", local);
900-
if frame != self.cur_frame() {
901-
write!(msg, " ({} frames up)", self.cur_frame() - frame).unwrap();
904+
if frame != self.frame_idx() {
905+
write!(msg, " ({} frames up)", self.frame_idx() - frame).unwrap();
902906
}
903907
write!(msg, ":").unwrap();
904908

src/librustc_mir/interpret/machine.rs

+39-28
Original file line numberDiff line numberDiff line change
@@ -120,16 +120,6 @@ pub trait Machine<'mir, 'tcx>: Sized {
120120
/// Whether memory accesses should be alignment-checked.
121121
fn enforce_alignment(memory_extra: &Self::MemoryExtra) -> bool;
122122

123-
/// Borrow the current thread's stack.
124-
fn stack(
125-
ecx: &'a InterpCx<'mir, 'tcx, Self>,
126-
) -> &'a [Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>];
127-
128-
/// Mutably borrow the current thread's stack.
129-
fn stack_mut(
130-
ecx: &'a mut InterpCx<'mir, 'tcx, Self>,
131-
) -> &'a mut Vec<Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>>;
132-
133123
/// Whether to enforce the validity invariant
134124
fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
135125

@@ -229,32 +219,43 @@ pub trait Machine<'mir, 'tcx>: Sized {
229219
Ok(())
230220
}
231221

232-
/// Called for *every* memory access to determine the real ID of the given
233-
/// allocation. This provides a way for the machine to "redirect" certain
234-
/// allocations as it sees fit.
222+
/// Called for *every* memory access to determine the real ID of the given allocation.
223+
/// This provides a way for the machine to "redirect" certain allocations as it sees fit.
235224
///
236-
/// This is used by Miri for two purposes:
237-
/// 1. Redirecting extern statics to real allocations.
238-
/// 2. Creating unique allocation ids for thread locals.
239-
///
240-
/// In Rust, one way for creating a thread local is by marking a static
241-
/// with `#[thread_local]`. On supported platforms this gets translated
242-
/// to a LLVM thread local. The problem with supporting these thread
243-
/// locals in Miri is that in the internals of the compiler they look as
244-
/// normal statics, except that they have the `thread_local` attribute.
245-
/// However, in Miri we want to have a property that each allocation has
246-
/// a unique id. Therefore, for these thread locals in
247-
/// `canonical_alloc_id` we reserve fresh allocation ids for each
248-
/// thread. Please note that `canonical_alloc_id` only reserves the
249-
/// allocation ids, the actual allocation for the thread local statics
250-
/// is done in the same way as for regular statics.
225+
/// This is used by Miri to redirect extern statics to real allocations.
251226
///
252227
/// This function must be idempotent.
253228
#[inline]
254229
fn canonical_alloc_id(_mem: &Memory<'mir, 'tcx, Self>, id: AllocId) -> AllocId {
255230
id
256231
}
257232

233+
/// Called when converting a `ty::Const` to an operand.
234+
///
235+
/// Miri uses this callback for creating unique allocation ids for thread
236+
/// locals. In Rust, one way for creating a thread local is by marking a
237+
/// static with `#[thread_local]`. On supported platforms this gets
238+
/// translated to a LLVM thread local for which LLVM automatically ensures
239+
/// that each thread gets its own copy. Since LLVM automatically handles
240+
/// thread locals, the Rust compiler just treats thread local statics as
241+
/// regular statics even though accessing a thread local static should be an
242+
/// effectful computation that depends on the current thread. The long term
243+
/// plan is to change MIR to make accesses to thread locals explicit
244+
/// (https://github.com/rust-lang/rust/issues/70685). While the issue 70685
245+
/// is not fixed, our current workaround in Miri is to use this function to
246+
/// reserve fresh allocation ids for each thread. Please note that here we
247+
/// only **reserve** the allocation ids; the actual allocation for the
248+
/// thread local statics is done in `Memory::get_global_alloc`, which uses
249+
/// `resolve_maybe_global_alloc` to retrieve information about the
250+
/// allocation id we generated.
251+
#[inline]
252+
fn eval_maybe_thread_local_static_const(
253+
_ecx: &InterpCx<'mir, 'tcx, Self>,
254+
val: mir::interpret::ConstValue<'tcx>,
255+
) -> InterpResult<'tcx, mir::interpret::ConstValue<'tcx>> {
256+
Ok(val)
257+
}
258+
258259
/// Called to obtain the `GlobalAlloc` associated with the allocation id.
259260
///
260261
/// Miri uses this callback to resolve the information about the original
@@ -326,6 +327,16 @@ pub trait Machine<'mir, 'tcx>: Sized {
326327
frame: Frame<'mir, 'tcx, Self::PointerTag>,
327328
) -> InterpResult<'tcx, Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>>;
328329

330+
/// Borrow the current thread's stack.
331+
fn stack(
332+
ecx: &'a InterpCx<'mir, 'tcx, Self>,
333+
) -> &'a [Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>];
334+
335+
/// Mutably borrow the current thread's stack.
336+
fn stack_mut(
337+
ecx: &'a mut InterpCx<'mir, 'tcx, Self>,
338+
) -> &'a mut Vec<Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>>;
339+
329340
/// Called immediately after a stack frame got pushed and its locals got initialized.
330341
fn after_stack_push(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
331342
Ok(())

src/librustc_mir/interpret/memory.rs

+10-8
Original file line numberDiff line numberDiff line change
@@ -430,11 +430,12 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
430430
is_write: bool,
431431
) -> InterpResult<'tcx, Cow<'tcx, Allocation<M::PointerTag, M::AllocExtra>>> {
432432
// The call to `resolve_maybe_global_alloc` is needed to enable Miri to
433-
// support thread local statics. In `M::canonical_alloc_id`, for a
434-
// thread local static, Miri reserves a fresh allocation id, but the
435-
// actual allocation is left to the code that handles statics which
436-
// calls this function (`get_global_alloc`). Since the allocation id is
437-
// fresh, it has no information about the original static. The call to
433+
// support thread local statics. In
434+
// `M::eval_maybe_thread_local_static_const`, for a thread local static,
435+
// Miri reserves a fresh allocation id, but the actual allocation is
436+
// left to the code that handles statics which calls this function
437+
// (`get_global_alloc`). Since the allocation id is fresh, it has no
438+
// information about the original static. The call to
438439
// `resolve_maybe_global_alloc` allows Miri to retrieve this information
439440
// for us.
440441
let (alloc, def_id) = match M::resolve_maybe_global_alloc(tcx, memory_extra, id) {
@@ -598,9 +599,10 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
598599

599600
// # Statics
600601
// The call to `resolve_maybe_global_alloc` is needed here because Miri
601-
// via the call to `canonical_alloc_id` above reserves fresh allocation
602-
// ids for thread local statics. However, the actual allocation is done
603-
// not in `canonical_alloc_id`, but in `get_raw` and `get_raw_mut`.
602+
// via the callback to `eval_maybe_thread_local_static_const` in
603+
// `eval_const_to_op` reserves fresh allocation ids for thread local
604+
// statics. However, the actual allocation is done not in
605+
// `resolve_maybe_global_alloc`, but in `get_raw` and `get_raw_mut`.
604606
// Since this function may get called before `get_raw`, we need to allow
605607
// Miri to retrieve the information about the static for us.
606608
match M::resolve_maybe_global_alloc(self.tcx, &self.extra, id) {

src/librustc_mir/interpret/operand.rs

+1
Original file line numberDiff line numberDiff line change
@@ -537,6 +537,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
537537
}
538538
ty::ConstKind::Value(val_val) => val_val,
539539
};
540+
let val_val = M::eval_maybe_thread_local_static_const(self, val_val)?;
540541
// Other cases need layout.
541542
let layout = from_known_layout(self.tcx, layout, || self.layout_of(val.ty))?;
542543
let op = match val_val {

src/librustc_mir/interpret/place.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -662,7 +662,7 @@ where
662662
}
663663
local => PlaceTy {
664664
// This works even for dead/uninitialized locals; we check further when writing
665-
place: Place::Local { frame: self.cur_frame(), local },
665+
place: Place::Local { frame: self.frame_idx(), local },
666666
layout: self.layout_of_local(self.frame(), local, None)?,
667667
},
668668
};

src/librustc_mir/interpret/step.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -60,18 +60,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
6060
let body = self.body();
6161
let basic_block = &body.basic_blocks()[block];
6262

63-
let old_frames = self.cur_frame();
63+
let old_frames = self.frame_idx();
6464

6565
if let Some(stmt) = basic_block.statements.get(stmt_id) {
66-
assert_eq!(old_frames, self.cur_frame());
66+
assert_eq!(old_frames, self.frame_idx());
6767
self.statement(stmt)?;
6868
return Ok(true);
6969
}
7070

7171
M::before_terminator(self)?;
7272

7373
let terminator = basic_block.terminator();
74-
assert_eq!(old_frames, self.cur_frame());
74+
assert_eq!(old_frames, self.frame_idx());
7575
self.terminator(terminator)?;
7676
Ok(true)
7777
}
@@ -84,7 +84,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
8484

8585
// Some statements (e.g., box) push new stack frames.
8686
// We have to record the stack frame number *before* executing the statement.
87-
let frame_idx = self.cur_frame();
87+
let frame_idx = self.frame_idx();
8888

8989
match &stmt.kind {
9090
Assign(box (place, rvalue)) => self.eval_rvalue_into_place(rvalue, *place)?,

src/librustc_mir/interpret/terminator.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
5252
}
5353

5454
Call { ref func, ref args, destination, ref cleanup, .. } => {
55-
let old_stack = self.cur_frame();
55+
let old_stack = self.frame_idx();
5656
let old_bb = self.frame().block;
5757
let func = self.eval_operand(func, None)?;
5858
let (fn_val, abi) = match func.layout.ty.kind {
@@ -80,7 +80,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
8080
self.eval_fn_call(fn_val, abi, &args[..], ret, *cleanup)?;
8181
// Sanity-check that `eval_fn_call` either pushed a new frame or
8282
// did a jump to another block.
83-
if self.cur_frame() == old_stack && self.frame().block == old_bb {
83+
if self.frame_idx() == old_stack && self.frame().block == old_bb {
8484
span_bug!(terminator.source_info.span, "evaluating this call made no progress");
8585
}
8686
}

0 commit comments

Comments
 (0)