Skip to content

Commit dcd5d7e

Browse files
authoredMay 28, 2024
Rollup merge of rust-lang#125633 - RalfJung:miri-no-copy, r=saethlin
miri: avoid making a full copy of all new allocations Hopefully fixes rust-lang/miri#3637 r? `@saethlin`
2 parents 7c4faa0 + 8693064 commit dcd5d7e

File tree

8 files changed

+92
-85
lines changed

8 files changed

+92
-85
lines changed
 

‎compiler/rustc_const_eval/src/const_eval/dummy_machine.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ impl<'tcx> interpret::Machine<'tcx> for DummyMachine {
174174
unimplemented!()
175175
}
176176

177-
fn init_frame_extra(
177+
fn init_frame(
178178
_ecx: &mut InterpCx<'tcx, Self>,
179179
_frame: interpret::Frame<'tcx, Self::Provenance>,
180180
) -> interpret::InterpResult<'tcx, interpret::Frame<'tcx, Self::Provenance, Self::FrameExtra>>

‎compiler/rustc_const_eval/src/const_eval/machine.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -643,7 +643,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeInterpreter<'tcx> {
643643
}
644644

645645
#[inline(always)]
646-
fn init_frame_extra(
646+
fn init_frame(
647647
ecx: &mut InterpCx<'tcx, Self>,
648648
frame: Frame<'tcx>,
649649
) -> InterpResult<'tcx, Frame<'tcx>> {

‎compiler/rustc_const_eval/src/interpret/eval_context.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -819,7 +819,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
819819
tracing_span: SpanGuard::new(),
820820
extra: (),
821821
};
822-
let frame = M::init_frame_extra(self, pre_frame)?;
822+
let frame = M::init_frame(self, pre_frame)?;
823823
self.stack_mut().push(frame);
824824

825825
// Make sure all the constants required by this frame evaluate successfully (post-monomorphization check).

‎compiler/rustc_const_eval/src/interpret/machine.rs

+41-17
Original file line numberDiff line numberDiff line change
@@ -329,27 +329,41 @@ pub trait Machine<'tcx>: Sized {
329329
ptr: Pointer<Self::Provenance>,
330330
) -> Option<(AllocId, Size, Self::ProvenanceExtra)>;
331331

332-
/// Called to adjust allocations to the Provenance and AllocExtra of this machine.
332+
/// Called to adjust global allocations to the Provenance and AllocExtra of this machine.
333333
///
334334
/// If `alloc` contains pointers, then they are all pointing to globals.
335335
///
336-
/// The way we construct allocations is to always first construct it without extra and then add
337-
/// the extra. This keeps uniform code paths for handling both allocations created by CTFE for
338-
/// globals, and allocations created by Miri during evaluation.
339-
///
340-
/// `kind` is the kind of the allocation being adjusted; it can be `None` when
341-
/// it's a global and `GLOBAL_KIND` is `None`.
342-
///
343336
/// This should avoid copying if no work has to be done! If this returns an owned
344337
/// allocation (because a copy had to be done to adjust things), machine memory will
345338
/// cache the result. (This relies on `AllocMap::get_or` being able to add the
346339
/// owned allocation to the map even when the map is shared.)
347-
fn adjust_allocation<'b>(
340+
fn adjust_global_allocation<'b>(
348341
ecx: &InterpCx<'tcx, Self>,
349342
id: AllocId,
350-
alloc: Cow<'b, Allocation>,
351-
kind: Option<MemoryKind<Self::MemoryKind>>,
352-
) -> InterpResult<'tcx, Cow<'b, Allocation<Self::Provenance, Self::AllocExtra, Self::Bytes>>>;
343+
alloc: &'b Allocation,
344+
) -> InterpResult<'tcx, Cow<'b, Allocation<Self::Provenance, Self::AllocExtra, Self::Bytes>>>
345+
{
346+
// The default implementation does a copy; CTFE machines have a more efficient implementation
347+
// based on their particular choice for `Provenance`, `AllocExtra`, and `Bytes`.
348+
let kind = Self::GLOBAL_KIND
349+
.expect("if GLOBAL_KIND is None, adjust_global_allocation must be overwritten");
350+
let alloc = alloc.adjust_from_tcx(&ecx.tcx, |ptr| ecx.global_root_pointer(ptr))?;
351+
let extra =
352+
Self::init_alloc_extra(ecx, id, MemoryKind::Machine(kind), alloc.size(), alloc.align)?;
353+
Ok(Cow::Owned(alloc.with_extra(extra)))
354+
}
355+
356+
/// Initialize the extra state of an allocation.
357+
///
358+
/// This is guaranteed to be called exactly once on all allocations that are accessed by the
359+
/// program.
360+
fn init_alloc_extra(
361+
ecx: &InterpCx<'tcx, Self>,
362+
id: AllocId,
363+
kind: MemoryKind<Self::MemoryKind>,
364+
size: Size,
365+
align: Align,
366+
) -> InterpResult<'tcx, Self::AllocExtra>;
353367

354368
/// Return a "root" pointer for the given allocation: the one that is used for direct
355369
/// accesses to this static/const/fn allocation, or the one returned from the heap allocator.
@@ -473,7 +487,7 @@ pub trait Machine<'tcx>: Sized {
473487
}
474488

475489
/// Called immediately before a new stack frame gets pushed.
476-
fn init_frame_extra(
490+
fn init_frame(
477491
ecx: &mut InterpCx<'tcx, Self>,
478492
frame: Frame<'tcx, Self::Provenance>,
479493
) -> InterpResult<'tcx, Frame<'tcx, Self::Provenance, Self::FrameExtra>>;
@@ -590,13 +604,23 @@ pub macro compile_time_machine(<$tcx: lifetime>) {
590604
}
591605

592606
#[inline(always)]
593-
fn adjust_allocation<'b>(
607+
fn adjust_global_allocation<'b>(
594608
_ecx: &InterpCx<$tcx, Self>,
595609
_id: AllocId,
596-
alloc: Cow<'b, Allocation>,
597-
_kind: Option<MemoryKind<Self::MemoryKind>>,
610+
alloc: &'b Allocation,
598611
) -> InterpResult<$tcx, Cow<'b, Allocation<Self::Provenance>>> {
599-
Ok(alloc)
612+
// Overwrite default implementation: no need to adjust anything.
613+
Ok(Cow::Borrowed(alloc))
614+
}
615+
616+
fn init_alloc_extra(
617+
_ecx: &InterpCx<$tcx, Self>,
618+
_id: AllocId,
619+
_kind: MemoryKind<Self::MemoryKind>,
620+
_size: Size,
621+
_align: Align,
622+
) -> InterpResult<$tcx, Self::AllocExtra> {
623+
Ok(())
600624
}
601625

602626
fn extern_static_pointer(

‎compiler/rustc_const_eval/src/interpret/memory.rs

+8-6
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
239239

240240
pub fn allocate_raw_ptr(
241241
&mut self,
242-
alloc: Allocation,
242+
alloc: Allocation<M::Provenance, (), M::Bytes>,
243243
kind: MemoryKind<M::MemoryKind>,
244244
) -> InterpResult<'tcx, Pointer<M::Provenance>> {
245245
let id = self.tcx.reserve_alloc_id();
@@ -248,8 +248,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
248248
M::GLOBAL_KIND.map(MemoryKind::Machine),
249249
"dynamically allocating global memory"
250250
);
251-
let alloc = M::adjust_allocation(self, id, Cow::Owned(alloc), Some(kind))?;
252-
self.memory.alloc_map.insert(id, (kind, alloc.into_owned()));
251+
// We have set things up so we don't need to call `adjust_from_tcx` here,
252+
// so we avoid copying the entire allocation contents.
253+
let extra = M::init_alloc_extra(self, id, kind, alloc.size(), alloc.align)?;
254+
let alloc = alloc.with_extra(extra);
255+
self.memory.alloc_map.insert(id, (kind, alloc));
253256
M::adjust_alloc_root_pointer(self, Pointer::from(id), Some(kind))
254257
}
255258

@@ -583,11 +586,10 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
583586
};
584587
M::before_access_global(self.tcx, &self.machine, id, alloc, def_id, is_write)?;
585588
// We got tcx memory. Let the machine initialize its "extra" stuff.
586-
M::adjust_allocation(
589+
M::adjust_global_allocation(
587590
self,
588591
id, // always use the ID we got as input, not the "hidden" one.
589-
Cow::Borrowed(alloc.inner()),
590-
M::GLOBAL_KIND.map(MemoryKind::Machine),
592+
alloc.inner(),
591593
)
592594
}
593595

‎compiler/rustc_middle/src/mir/interpret/allocation.rs

+21-22
Original file line numberDiff line numberDiff line change
@@ -266,19 +266,6 @@ impl AllocRange {
266266

267267
// The constructors are all without extra; the extra gets added by a machine hook later.
268268
impl<Prov: Provenance, Bytes: AllocBytes> Allocation<Prov, (), Bytes> {
269-
/// Creates an allocation from an existing `Bytes` value - this is needed for miri FFI support
270-
pub fn from_raw_bytes(bytes: Bytes, align: Align, mutability: Mutability) -> Self {
271-
let size = Size::from_bytes(bytes.len());
272-
Self {
273-
bytes,
274-
provenance: ProvenanceMap::new(),
275-
init_mask: InitMask::new(size, true),
276-
align,
277-
mutability,
278-
extra: (),
279-
}
280-
}
281-
282269
/// Creates an allocation initialized by the given bytes
283270
pub fn from_bytes<'a>(
284271
slice: impl Into<Cow<'a, [u8]>>,
@@ -342,18 +329,30 @@ impl<Prov: Provenance, Bytes: AllocBytes> Allocation<Prov, (), Bytes> {
342329
Err(x) => x,
343330
}
344331
}
332+
333+
/// Add the extra.
334+
pub fn with_extra<Extra>(self, extra: Extra) -> Allocation<Prov, Extra, Bytes> {
335+
Allocation {
336+
bytes: self.bytes,
337+
provenance: self.provenance,
338+
init_mask: self.init_mask,
339+
align: self.align,
340+
mutability: self.mutability,
341+
extra,
342+
}
343+
}
345344
}
346345

347346
impl Allocation {
348347
/// Adjust allocation from the ones in `tcx` to a custom Machine instance
349-
/// with a different `Provenance`, `Extra` and `Byte` type.
350-
pub fn adjust_from_tcx<Prov: Provenance, Extra, Bytes: AllocBytes, Err>(
351-
self,
348+
/// with a different `Provenance` and `Byte` type.
349+
pub fn adjust_from_tcx<Prov: Provenance, Bytes: AllocBytes, Err>(
350+
&self,
352351
cx: &impl HasDataLayout,
353-
extra: Extra,
354352
mut adjust_ptr: impl FnMut(Pointer<CtfeProvenance>) -> Result<Pointer<Prov>, Err>,
355-
) -> Result<Allocation<Prov, Extra, Bytes>, Err> {
356-
let mut bytes = self.bytes;
353+
) -> Result<Allocation<Prov, (), Bytes>, Err> {
354+
// Copy the data.
355+
let mut bytes = Bytes::from_bytes(Cow::Borrowed(&*self.bytes), self.align);
357356
// Adjust provenance of pointers stored in this allocation.
358357
let mut new_provenance = Vec::with_capacity(self.provenance.ptrs().len());
359358
let ptr_size = cx.data_layout().pointer_size.bytes_usize();
@@ -369,12 +368,12 @@ impl Allocation {
369368
}
370369
// Create allocation.
371370
Ok(Allocation {
372-
bytes: AllocBytes::from_bytes(Cow::Owned(Vec::from(bytes)), self.align),
371+
bytes,
373372
provenance: ProvenanceMap::from_presorted_ptrs(new_provenance),
374-
init_mask: self.init_mask,
373+
init_mask: self.init_mask.clone(),
375374
align: self.align,
376375
mutability: self.mutability,
377-
extra,
376+
extra: self.extra,
378377
})
379378
}
380379
}

‎src/tools/miri/src/concurrency/thread.rs

+7-6
Original file line numberDiff line numberDiff line change
@@ -862,14 +862,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
862862
if tcx.is_foreign_item(def_id) {
863863
throw_unsup_format!("foreign thread-local statics are not supported");
864864
}
865-
let allocation = this.ctfe_query(|tcx| tcx.eval_static_initializer(def_id))?;
866-
let mut allocation = allocation.inner().clone();
865+
let alloc = this.ctfe_query(|tcx| tcx.eval_static_initializer(def_id))?;
866+
// We make a full copy of this allocation.
867+
let mut alloc = alloc.inner().adjust_from_tcx(&this.tcx, |ptr| this.global_root_pointer(ptr))?;
867868
// This allocation will be deallocated when the thread dies, so it is not in read-only memory.
868-
allocation.mutability = Mutability::Mut;
869+
alloc.mutability = Mutability::Mut;
869870
// Create a fresh allocation with this content.
870-
let new_alloc = this.allocate_raw_ptr(allocation, MiriMemoryKind::Tls.into())?;
871-
this.machine.threads.set_thread_local_alloc(def_id, new_alloc);
872-
Ok(new_alloc)
871+
let ptr = this.allocate_raw_ptr(alloc, MiriMemoryKind::Tls.into())?;
872+
this.machine.threads.set_thread_local_alloc(def_id, ptr);
873+
Ok(ptr)
873874
}
874875
}
875876

‎src/tools/miri/src/machine.rs

+12-31
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
//! Global machine state as well as implementation of the interpreter engine
22
//! `Machine` trait.
33
4-
use std::borrow::Cow;
54
use std::cell::RefCell;
65
use std::collections::hash_map::Entry;
76
use std::fmt;
@@ -1086,40 +1085,33 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
10861085
}
10871086
}
10881087

1089-
fn adjust_allocation<'b>(
1088+
fn init_alloc_extra(
10901089
ecx: &MiriInterpCx<'tcx>,
10911090
id: AllocId,
1092-
alloc: Cow<'b, Allocation>,
1093-
kind: Option<MemoryKind>,
1094-
) -> InterpResult<'tcx, Cow<'b, Allocation<Self::Provenance, Self::AllocExtra, Self::Bytes>>>
1095-
{
1096-
let kind = kind.expect("we set our STATIC_KIND so this cannot be None");
1091+
kind: MemoryKind,
1092+
size: Size,
1093+
align: Align,
1094+
) -> InterpResult<'tcx, Self::AllocExtra> {
10971095
if ecx.machine.tracked_alloc_ids.contains(&id) {
1098-
ecx.emit_diagnostic(NonHaltingDiagnostic::CreatedAlloc(
1099-
id,
1100-
alloc.size(),
1101-
alloc.align,
1102-
kind,
1103-
));
1096+
ecx.emit_diagnostic(NonHaltingDiagnostic::CreatedAlloc(id, size, align, kind));
11041097
}
11051098

1106-
let alloc = alloc.into_owned();
11071099
let borrow_tracker = ecx
11081100
.machine
11091101
.borrow_tracker
11101102
.as_ref()
1111-
.map(|bt| bt.borrow_mut().new_allocation(id, alloc.size(), kind, &ecx.machine));
1103+
.map(|bt| bt.borrow_mut().new_allocation(id, size, kind, &ecx.machine));
11121104

1113-
let race_alloc = ecx.machine.data_race.as_ref().map(|data_race| {
1105+
let data_race = ecx.machine.data_race.as_ref().map(|data_race| {
11141106
data_race::AllocState::new_allocation(
11151107
data_race,
11161108
&ecx.machine.threads,
1117-
alloc.size(),
1109+
size,
11181110
kind,
11191111
ecx.machine.current_span(),
11201112
)
11211113
});
1122-
let buffer_alloc = ecx.machine.weak_memory.then(weak_memory::AllocState::new_allocation);
1114+
let weak_memory = ecx.machine.weak_memory.then(weak_memory::AllocState::new_allocation);
11231115

11241116
// If an allocation is leaked, we want to report a backtrace to indicate where it was
11251117
// allocated. We don't need to record a backtrace for allocations which are allowed to
@@ -1130,25 +1122,14 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
11301122
Some(ecx.generate_stacktrace())
11311123
};
11321124

1133-
let alloc: Allocation<Provenance, Self::AllocExtra, Self::Bytes> = alloc.adjust_from_tcx(
1134-
&ecx.tcx,
1135-
AllocExtra {
1136-
borrow_tracker,
1137-
data_race: race_alloc,
1138-
weak_memory: buffer_alloc,
1139-
backtrace,
1140-
},
1141-
|ptr| ecx.global_root_pointer(ptr),
1142-
)?;
1143-
11441125
if matches!(kind, MemoryKind::Machine(kind) if kind.should_save_allocation_span()) {
11451126
ecx.machine
11461127
.allocation_spans
11471128
.borrow_mut()
11481129
.insert(id, (ecx.machine.current_span(), None));
11491130
}
11501131

1151-
Ok(Cow::Owned(alloc))
1132+
Ok(AllocExtra { borrow_tracker, data_race, weak_memory, backtrace })
11521133
}
11531134

11541135
fn adjust_alloc_root_pointer(
@@ -1357,7 +1338,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
13571338
}
13581339

13591340
#[inline(always)]
1360-
fn init_frame_extra(
1341+
fn init_frame(
13611342
ecx: &mut InterpCx<'tcx, Self>,
13621343
frame: Frame<'tcx, Provenance>,
13631344
) -> InterpResult<'tcx, Frame<'tcx, Provenance, FrameExtra<'tcx>>> {

0 commit comments

Comments
 (0)