Skip to content

Commit 4a90e36

Browse files
committed
Auto merge of rust-lang#74775 - RalfJung:miri-alloc-ids, r=oli-obk
Miri: replace canonical_alloc_id mechanism by extern_static_alloc_id We only have to call `extern_static_alloc_id` when a `Pointer` is "imported" from the `tcx` to the machine, not on each access. Also drop the old hook for TLS handling, it is not needed any more. The Miri side of this is at rust-lang/miri#1489. Fixes rust-lang#71194 r? @oli-obk
2 parents 52d2c7a + b8fd0f6 commit 4a90e36

File tree

12 files changed

+122
-122
lines changed

12 files changed

+122
-122
lines changed

src/librustc_middle/mir/interpret/error.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -502,8 +502,6 @@ impl fmt::Display for UndefinedBehaviorInfo<'_> {
502502
pub enum UnsupportedOpInfo {
503503
/// Free-form case. Only for errors that are never caught!
504504
Unsupported(String),
505-
/// Accessing an unsupported foreign static.
506-
ReadForeignStatic(DefId),
507505
/// Could not find MIR for a function.
508506
NoMirFor(DefId),
509507
/// Encountered a pointer where we needed raw bytes.
@@ -515,16 +513,16 @@ pub enum UnsupportedOpInfo {
515513
ReadBytesAsPointer,
516514
/// Accessing thread local statics
517515
ThreadLocalStatic(DefId),
516+
/// Accessing an unsupported extern static.
517+
ReadExternStatic(DefId),
518518
}
519519

520520
impl fmt::Display for UnsupportedOpInfo {
521521
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
522522
use UnsupportedOpInfo::*;
523523
match self {
524524
Unsupported(ref msg) => write!(f, "{}", msg),
525-
ReadForeignStatic(did) => {
526-
write!(f, "cannot read from foreign (extern) static ({:?})", did)
527-
}
525+
ReadExternStatic(did) => write!(f, "cannot read from extern static ({:?})", did),
528526
NoMirFor(did) => write!(f, "no MIR body is available for {:?}", did),
529527
ReadPointerAsBytes => write!(f, "unable to turn pointer into raw bytes",),
530528
ReadBytesAsPointer => write!(f, "unable to turn bytes into a pointer"),

src/librustc_mir/const_eval/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ pub(crate) fn destructure_const<'tcx>(
4141
) -> mir::DestructuredConst<'tcx> {
4242
trace!("destructure_const: {:?}", val);
4343
let ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
44-
let op = ecx.eval_const_to_op(val, None).unwrap();
44+
let op = ecx.const_to_op(val, None).unwrap();
4545

4646
// We go to `usize` as we cannot allocate anything bigger anyway.
4747
let (field_count, variant, down) = match val.ty.kind {

src/librustc_mir/interpret/eval_context.rs

+12-9
Original file line numberDiff line numberDiff line change
@@ -323,14 +323,17 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
323323
}
324324

325325
/// Call this to turn untagged "global" pointers (obtained via `tcx`) into
326-
/// the *canonical* machine pointer to the allocation. Must never be used
327-
/// for any other pointers!
326+
/// the machine pointer to the allocation. Must never be used
327+
/// for any other pointers, nor for TLS statics.
328328
///
329-
/// This represents a *direct* access to that memory, as opposed to access
330-
/// through a pointer that was created by the program.
329+
/// Using the resulting pointer represents a *direct* access to that memory
330+
/// (e.g. by directly using a `static`),
331+
/// as opposed to access through a pointer that was created by the program.
332+
///
333+
/// This function can fail only if `ptr` points to an `extern static`.
331334
#[inline(always)]
332-
pub fn tag_global_base_pointer(&self, ptr: Pointer) -> Pointer<M::PointerTag> {
333-
self.memory.tag_global_base_pointer(ptr)
335+
pub fn global_base_pointer(&self, ptr: Pointer) -> InterpResult<'tcx, Pointer<M::PointerTag>> {
336+
self.memory.global_base_pointer(ptr)
334337
}
335338

336339
#[inline(always)]
@@ -845,12 +848,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
845848
};
846849
let val = self.tcx.const_eval_global_id(param_env, gid, Some(self.tcx.span))?;
847850

848-
// Even though `ecx.const_eval` is called from `eval_const_to_op` we can never have a
851+
// Even though `ecx.const_eval` is called from `const_to_op` we can never have a
849852
// recursion deeper than one level, because the `tcx.const_eval` above is guaranteed to not
850-
// return `ConstValue::Unevaluated`, which is the only way that `eval_const_to_op` will call
853+
// return `ConstValue::Unevaluated`, which is the only way that `const_to_op` will call
851854
// `ecx.const_eval`.
852855
let const_ = ty::Const { val: ty::ConstKind::Value(val), ty };
853-
self.eval_const_to_op(&const_, None)
856+
self.const_to_op(&const_, None)
854857
}
855858

856859
pub fn const_eval_raw(

src/librustc_mir/interpret/machine.rs

+20-49
Original file line numberDiff line numberDiff line change
@@ -238,45 +238,30 @@ pub trait Machine<'mir, 'tcx>: Sized {
238238
Ok(())
239239
}
240240

241-
/// Called for *every* memory access to determine the real ID of the given allocation.
242-
/// This provides a way for the machine to "redirect" certain allocations as it sees fit.
243-
///
244-
/// This is used by Miri to redirect extern statics to real allocations.
245-
///
246-
/// This function must be idempotent.
247-
#[inline]
248-
fn canonical_alloc_id(_mem: &Memory<'mir, 'tcx, Self>, id: AllocId) -> AllocId {
249-
id
241+
/// Return the `AllocId` for the given thread-local static in the current thread.
242+
fn thread_local_static_alloc_id(
243+
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
244+
def_id: DefId,
245+
) -> InterpResult<'tcx, AllocId> {
246+
throw_unsup!(ThreadLocalStatic(def_id))
250247
}
251248

252-
/// Called when converting a `ty::Const` to an operand (in
253-
/// `eval_const_to_op`).
254-
///
255-
/// Miri uses this callback for creating per thread allocations for thread
256-
/// locals. In Rust, one way of creating a thread local is by marking a
257-
/// static with `#[thread_local]`. On supported platforms this gets
258-
/// translated to a LLVM thread local for which LLVM automatically ensures
259-
/// that each thread gets its own copy. Since LLVM automatically handles
260-
/// thread locals, the Rust compiler just treats thread local statics as
261-
/// regular statics even though accessing a thread local static should be an
262-
/// effectful computation that depends on the current thread. The long term
263-
/// plan is to change MIR to make accesses to thread locals explicit
264-
/// (https://github.com/rust-lang/rust/issues/70685). While the issue 70685
265-
/// is not fixed, our current workaround in Miri is to use this function to
266-
/// make per-thread copies of thread locals. Please note that we cannot make
267-
/// these copies in `canonical_alloc_id` because that is too late: for
268-
/// example, if one created a pointer in thread `t1` to a thread local and
269-
/// sent it to another thread `t2`, resolving the access in
270-
/// `canonical_alloc_id` would result in pointer pointing to `t2`'s thread
271-
/// local and not `t1` as it should.
272-
#[inline]
273-
fn adjust_global_const(
274-
_ecx: &InterpCx<'mir, 'tcx, Self>,
275-
val: mir::interpret::ConstValue<'tcx>,
276-
) -> InterpResult<'tcx, mir::interpret::ConstValue<'tcx>> {
277-
Ok(val)
249+
/// Return the `AllocId` backing the given `extern static`.
250+
fn extern_static_alloc_id(
251+
mem: &Memory<'mir, 'tcx, Self>,
252+
def_id: DefId,
253+
) -> InterpResult<'tcx, AllocId> {
254+
// Use the `AllocId` associated with the `DefId`. Any actual *access* will fail.
255+
Ok(mem.tcx.create_static_alloc(def_id))
278256
}
279257

258+
/// Return the "base" tag for the given *global* allocation: the one that is used for direct
259+
/// accesses to this static/const/fn allocation. If `id` is not a global allocation,
260+
/// this will return an unusable tag (i.e., accesses will be UB)!
261+
///
262+
/// Called on the id returned by `thread_local_static_alloc_id` and `extern_static_alloc_id`, if needed.
263+
fn tag_global_base_pointer(memory_extra: &Self::MemoryExtra, id: AllocId) -> Self::PointerTag;
264+
280265
/// Called to initialize the "extra" state of an allocation and make the pointers
281266
/// it contains (in relocations) tagged. The way we construct allocations is
282267
/// to always first construct it without extra and then add the extra.
@@ -309,13 +294,6 @@ pub trait Machine<'mir, 'tcx>: Sized {
309294
Ok(())
310295
}
311296

312-
/// Return the "base" tag for the given *global* allocation: the one that is used for direct
313-
/// accesses to this static/const/fn allocation. If `id` is not a global allocation,
314-
/// this will return an unusable tag (i.e., accesses will be UB)!
315-
///
316-
/// Expects `id` to be already canonical, if needed.
317-
fn tag_global_base_pointer(memory_extra: &Self::MemoryExtra, id: AllocId) -> Self::PointerTag;
318-
319297
/// Executes a retagging operation
320298
#[inline]
321299
fn retag(
@@ -375,13 +353,6 @@ pub trait Machine<'mir, 'tcx>: Sized {
375353
_mem: &Memory<'mir, 'tcx, Self>,
376354
_ptr: Pointer<Self::PointerTag>,
377355
) -> InterpResult<'tcx, u64>;
378-
379-
fn thread_local_alloc_id(
380-
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
381-
did: DefId,
382-
) -> InterpResult<'tcx, AllocId> {
383-
throw_unsup!(ThreadLocalStatic(did))
384-
}
385356
}
386357

387358
// A lot of the flexibility above is just needed for `Miri`, but all "compile-time" machines

src/librustc_mir/interpret/memory.rs

+51-35
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use std::ptr;
1414

1515
use rustc_ast::ast::Mutability;
1616
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
17+
use rustc_hir::def_id::DefId;
1718
use rustc_middle::ty::{self, Instance, ParamEnv, TyCtxt};
1819
use rustc_target::abi::{Align, HasDataLayout, Size, TargetDataLayout};
1920

@@ -118,6 +119,17 @@ pub struct Memory<'mir, 'tcx, M: Machine<'mir, 'tcx>> {
118119
pub tcx: TyCtxt<'tcx>,
119120
}
120121

122+
/// Return the `tcx` allocation containing the initial value of the given static
123+
pub fn get_static(tcx: TyCtxt<'tcx>, def_id: DefId) -> InterpResult<'tcx, &'tcx Allocation> {
124+
trace!("get_static: Need to compute {:?}", def_id);
125+
let instance = Instance::mono(tcx, def_id);
126+
let gid = GlobalId { instance, promoted: None };
127+
// Use the raw query here to break validation cycles. Later uses of the static
128+
// will call the full query anyway.
129+
let raw_const = tcx.const_eval_raw(ty::ParamEnv::reveal_all().and(gid))?;
130+
Ok(tcx.global_alloc(raw_const.alloc_id).unwrap_memory())
131+
}
132+
121133
impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for Memory<'mir, 'tcx, M> {
122134
#[inline]
123135
fn data_layout(&self) -> &TargetDataLayout {
@@ -137,15 +149,36 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
137149
}
138150

139151
/// Call this to turn untagged "global" pointers (obtained via `tcx`) into
140-
/// the *canonical* machine pointer to the allocation. Must never be used
141-
/// for any other pointers!
152+
/// the machine pointer to the allocation. Must never be used
153+
/// for any other pointers, nor for TLS statics.
154+
///
155+
/// Using the resulting pointer represents a *direct* access to that memory
156+
/// (e.g. by directly using a `static`),
157+
/// as opposed to access through a pointer that was created by the program.
142158
///
143-
/// This represents a *direct* access to that memory, as opposed to access
144-
/// through a pointer that was created by the program.
159+
/// This function can fail only if `ptr` points to an `extern static`.
145160
#[inline]
146-
pub fn tag_global_base_pointer(&self, ptr: Pointer) -> Pointer<M::PointerTag> {
147-
let id = M::canonical_alloc_id(self, ptr.alloc_id);
148-
ptr.with_tag(M::tag_global_base_pointer(&self.extra, id))
161+
pub fn global_base_pointer(
162+
&self,
163+
mut ptr: Pointer,
164+
) -> InterpResult<'tcx, Pointer<M::PointerTag>> {
165+
// We need to handle `extern static`.
166+
let ptr = match self.tcx.get_global_alloc(ptr.alloc_id) {
167+
Some(GlobalAlloc::Static(def_id)) if self.tcx.is_thread_local_static(def_id) => {
168+
bug!("global memory cannot point to thread-local static")
169+
}
170+
Some(GlobalAlloc::Static(def_id)) if self.tcx.is_foreign_item(def_id) => {
171+
ptr.alloc_id = M::extern_static_alloc_id(self, def_id)?;
172+
ptr
173+
}
174+
_ => {
175+
// No need to change the `AllocId`.
176+
ptr
177+
}
178+
};
179+
// And we need to get the tag.
180+
let tag = M::tag_global_base_pointer(&self.extra, ptr.alloc_id);
181+
Ok(ptr.with_tag(tag))
149182
}
150183

151184
pub fn create_fn_alloc(
@@ -162,7 +195,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
162195
id
163196
}
164197
};
165-
self.tag_global_base_pointer(Pointer::from(id))
198+
// Functions are global allocations, so make sure we get the right base pointer.
199+
// We know this is not an `extern static` so this cannot fail.
200+
self.global_base_pointer(Pointer::from(id)).unwrap()
166201
}
167202

168203
pub fn allocate(
@@ -195,6 +230,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
195230
M::GLOBAL_KIND.map(MemoryKind::Machine),
196231
"dynamically allocating global memory"
197232
);
233+
// This is a new allocation, not a new global one, so no `global_base_ptr`.
198234
let (alloc, tag) = M::init_allocation_extra(&self.extra, id, Cow::Owned(alloc), Some(kind));
199235
self.alloc_map.insert(id, (kind, alloc.into_owned()));
200236
Pointer::from(id).with_tag(tag)
@@ -437,6 +473,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
437473
Some(GlobalAlloc::Function(..)) => throw_ub!(DerefFunctionPointer(id)),
438474
None => throw_ub!(PointerUseAfterFree(id)),
439475
Some(GlobalAlloc::Static(def_id)) => {
476+
assert!(tcx.is_static(def_id));
440477
assert!(!tcx.is_thread_local_static(def_id));
441478
// Notice that every static has two `AllocId` that will resolve to the same
442479
// thing here: one maps to `GlobalAlloc::Static`, this is the "lazy" ID,
@@ -448,29 +485,11 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
448485
// The `GlobalAlloc::Memory` branch here is still reachable though; when a static
449486
// contains a reference to memory that was created during its evaluation (i.e., not
450487
// to another static), those inner references only exist in "resolved" form.
451-
//
452-
// Assumes `id` is already canonical.
453488
if tcx.is_foreign_item(def_id) {
454-
trace!("get_global_alloc: foreign item {:?}", def_id);
455-
throw_unsup!(ReadForeignStatic(def_id))
489+
throw_unsup!(ReadExternStatic(def_id));
456490
}
457-
trace!("get_global_alloc: Need to compute {:?}", def_id);
458-
let instance = Instance::mono(tcx, def_id);
459-
let gid = GlobalId { instance, promoted: None };
460-
// Use the raw query here to break validation cycles. Later uses of the static
461-
// will call the full query anyway.
462-
let raw_const =
463-
tcx.const_eval_raw(ty::ParamEnv::reveal_all().and(gid)).map_err(|err| {
464-
// no need to report anything, the const_eval call takes care of that
465-
// for statics
466-
assert!(tcx.is_static(def_id));
467-
err
468-
})?;
469-
// Make sure we use the ID of the resolved memory, not the lazy one!
470-
let id = raw_const.alloc_id;
471-
let allocation = tcx.global_alloc(id).unwrap_memory();
472-
473-
(allocation, Some(def_id))
491+
492+
(get_static(tcx, def_id)?, Some(def_id))
474493
}
475494
};
476495
M::before_access_global(memory_extra, id, alloc, def_id, is_write)?;
@@ -482,6 +501,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
482501
alloc,
483502
M::GLOBAL_KIND.map(MemoryKind::Machine),
484503
);
504+
// Sanity check that this is the same pointer we would have gotten via `global_base_pointer`.
485505
debug_assert_eq!(tag, M::tag_global_base_pointer(memory_extra, id));
486506
Ok(alloc)
487507
}
@@ -492,7 +512,6 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
492512
&self,
493513
id: AllocId,
494514
) -> InterpResult<'tcx, &Allocation<M::PointerTag, M::AllocExtra>> {
495-
let id = M::canonical_alloc_id(self, id);
496515
// The error type of the inner closure here is somewhat funny. We have two
497516
// ways of "erroring": An actual error, or because we got a reference from
498517
// `get_global_alloc` that we can actually use directly without inserting anything anywhere.
@@ -529,7 +548,6 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
529548
&mut self,
530549
id: AllocId,
531550
) -> InterpResult<'tcx, &mut Allocation<M::PointerTag, M::AllocExtra>> {
532-
let id = M::canonical_alloc_id(self, id);
533551
let tcx = self.tcx;
534552
let memory_extra = &self.extra;
535553
let a = self.alloc_map.get_mut_or(id, || {
@@ -568,7 +586,6 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
568586
id: AllocId,
569587
liveness: AllocCheck,
570588
) -> InterpResult<'static, (Size, Align)> {
571-
let id = M::canonical_alloc_id(self, id);
572589
// # Regular allocations
573590
// Don't use `self.get_raw` here as that will
574591
// a) cause cycles in case `id` refers to a static
@@ -621,7 +638,6 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
621638
}
622639
}
623640

624-
/// Assumes `id` is already canonical.
625641
fn get_fn_alloc(&self, id: AllocId) -> Option<FnVal<'tcx, M::ExtraFnVal>> {
626642
trace!("reading fn ptr: {}", id);
627643
if let Some(extra) = self.extra_fn_ptr_map.get(&id) {
@@ -642,8 +658,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
642658
if ptr.offset.bytes() != 0 {
643659
throw_ub!(InvalidFunctionPointer(ptr.erase_tag()))
644660
}
645-
let id = M::canonical_alloc_id(self, ptr.alloc_id);
646-
self.get_fn_alloc(id).ok_or_else(|| err_ub!(InvalidFunctionPointer(ptr.erase_tag())).into())
661+
self.get_fn_alloc(ptr.alloc_id)
662+
.ok_or_else(|| err_ub!(InvalidFunctionPointer(ptr.erase_tag())).into())
647663
}
648664

649665
pub fn mark_immutable(&mut self, id: AllocId) -> InterpResult<'tcx> {

src/librustc_mir/interpret/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ pub use rustc_middle::mir::interpret::*; // have all the `interpret` symbols in
2020
pub use self::eval_context::{Frame, InterpCx, LocalState, LocalValue, StackPopCleanup};
2121
pub use self::intern::{intern_const_alloc_recursive, InternKind};
2222
pub use self::machine::{compile_time_machine, AllocMap, Machine, MayLeak, StackPopJump};
23-
pub use self::memory::{AllocCheck, FnVal, Memory, MemoryKind};
23+
pub use self::memory::{get_static, AllocCheck, FnVal, Memory, MemoryKind};
2424
pub use self::operand::{ImmTy, Immediate, OpTy, Operand};
2525
pub use self::place::{MPlaceTy, MemPlace, MemPlaceMeta, Place, PlaceTy};
2626
pub use self::validity::RefTracking;

0 commit comments

Comments
 (0)