Skip to content

Commit 6f407d6

Browse files
committed
Allow non-Box allocations in preparation for aligned const allocations for miri. Credit to emarteca for the code.
1 parent 267cd1d commit 6f407d6

File tree

6 files changed

+120
-48
lines changed

6 files changed

+120
-48
lines changed

compiler/rustc_const_eval/src/interpret/machine.rs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use rustc_target::spec::abi::Abi as CallAbi;
1616
use crate::const_eval::CheckAlignment;
1717

1818
use super::{
19-
AllocId, AllocRange, Allocation, ConstAllocation, Frame, ImmTy, InterpCx, InterpResult,
19+
AllocId, AllocRange, Allocation, AllocBytes, ConstAllocation, Frame, ImmTy, InterpCx, InterpResult,
2020
MemoryKind, OpTy, Operand, PlaceTy, Pointer, Provenance, Scalar, StackPopUnwind,
2121
};
2222

@@ -105,10 +105,13 @@ pub trait Machine<'mir, 'tcx>: Sized {
105105
/// Extra data stored in every allocation.
106106
type AllocExtra: Debug + Clone + 'static;
107107

108+
/// Type for the bytes of the allocation.
109+
type Bytes: AllocBytes + 'static;
110+
108111
/// Memory's allocation map
109112
type MemoryMap: AllocMap<
110113
AllocId,
111-
(MemoryKind<Self::MemoryKind>, Allocation<Self::Provenance, Self::AllocExtra>),
114+
(MemoryKind<Self::MemoryKind>, Allocation<Self::Provenance, Self::AllocExtra, Self::Bytes>),
112115
> + Default
113116
+ Clone;
114117

@@ -338,7 +341,7 @@ pub trait Machine<'mir, 'tcx>: Sized {
338341
id: AllocId,
339342
alloc: Cow<'b, Allocation>,
340343
kind: Option<MemoryKind<Self::MemoryKind>>,
341-
) -> InterpResult<'tcx, Cow<'b, Allocation<Self::Provenance, Self::AllocExtra>>>;
344+
) -> InterpResult<'tcx, Cow<'b, Allocation<Self::Provenance, Self::AllocExtra, Self::Bytes>>>;
342345

343346
fn eval_inline_asm(
344347
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
@@ -459,6 +462,7 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) {
459462

460463
type AllocExtra = ();
461464
type FrameExtra = ();
465+
type Bytes = Box<[u8]>;
462466

463467
#[inline(always)]
464468
fn use_addr_for_alignment_check(_ecx: &InterpCx<$mir, $tcx, Self>) -> bool {

compiler/rustc_const_eval/src/interpret/memory.rs

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@ use rustc_target::abi::{Align, HasDataLayout, Size};
2121
use crate::const_eval::CheckAlignment;
2222

2323
use super::{
24-
alloc_range, AllocId, AllocMap, AllocRange, Allocation, CheckInAllocMsg, GlobalAlloc, InterpCx,
25-
InterpResult, Machine, MayLeak, Pointer, PointerArithmetic, Provenance, Scalar,
24+
alloc_range, AllocBytes, AllocId, AllocMap, AllocRange, Allocation, CheckInAllocMsg,
25+
GlobalAlloc, InterpCx, InterpResult, Machine, MayLeak, Pointer, PointerArithmetic, Provenance,
26+
Scalar,
2627
};
2728

2829
#[derive(Debug, PartialEq, Copy, Clone)]
@@ -114,16 +115,16 @@ pub struct Memory<'mir, 'tcx, M: Machine<'mir, 'tcx>> {
114115
/// A reference to some allocation that was already bounds-checked for the given region
115116
/// and had the on-access machine hooks run.
116117
#[derive(Copy, Clone)]
117-
pub struct AllocRef<'a, 'tcx, Prov: Provenance, Extra> {
118-
alloc: &'a Allocation<Prov, Extra>,
118+
pub struct AllocRef<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes = Box<[u8]>> {
119+
alloc: &'a Allocation<Prov, Extra, Bytes>,
119120
range: AllocRange,
120121
tcx: TyCtxt<'tcx>,
121122
alloc_id: AllocId,
122123
}
123124
/// A reference to some allocation that was already bounds-checked for the given region
124125
/// and had the on-access machine hooks run.
125-
pub struct AllocRefMut<'a, 'tcx, Prov: Provenance, Extra> {
126-
alloc: &'a mut Allocation<Prov, Extra>,
126+
pub struct AllocRefMut<'a, 'tcx, Prov: Provenance, Extra, Bytes: AllocBytes = Box<[u8]>> {
127+
alloc: &'a mut Allocation<Prov, Extra, Bytes>,
127128
range: AllocRange,
128129
tcx: TyCtxt<'tcx>,
129130
alloc_id: AllocId,
@@ -483,7 +484,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
483484
&self,
484485
id: AllocId,
485486
is_write: bool,
486-
) -> InterpResult<'tcx, Cow<'tcx, Allocation<M::Provenance, M::AllocExtra>>> {
487+
) -> InterpResult<'tcx, Cow<'tcx, Allocation<M::Provenance, M::AllocExtra, M::Bytes>>> {
487488
let (alloc, def_id) = match self.tcx.try_get_global_alloc(id) {
488489
Some(GlobalAlloc::Memory(mem)) => {
489490
// Memory of a constant or promoted or anonymous memory referenced by a static.
@@ -526,14 +527,25 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
526527
)
527528
}
528529

530+
/// Get the base address for the bytes in an `Allocation` specified by the
531+
/// `AllocID` passed in; error if no such allocation exists.
532+
///
533+
/// It is up to the caller to take sufficient care when using this address:
534+
/// there could be provenance or uninit memory in there, and other memory
535+
/// accesses could invalidate the exposed pointer.
536+
pub fn alloc_base_addr(&self, id: AllocId) -> InterpResult<'tcx, *const ()> {
537+
let alloc = self.get_alloc_raw(id)?;
538+
Ok(alloc.base_addr())
539+
}
540+
529541
/// Gives raw access to the `Allocation`, without bounds or alignment checks.
530542
/// The caller is responsible for calling the access hooks!
531543
///
532544
/// You almost certainly want to use `get_ptr_alloc`/`get_ptr_alloc_mut` instead.
533545
fn get_alloc_raw(
534546
&self,
535547
id: AllocId,
536-
) -> InterpResult<'tcx, &Allocation<M::Provenance, M::AllocExtra>> {
548+
) -> InterpResult<'tcx, &Allocation<M::Provenance, M::AllocExtra, M::Bytes>> {
537549
// The error type of the inner closure here is somewhat funny. We have two
538550
// ways of "erroring": An actual error, or because we got a reference from
539551
// `get_global_alloc` that we can actually use directly without inserting anything anywhere.
@@ -569,7 +581,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
569581
ptr: Pointer<Option<M::Provenance>>,
570582
size: Size,
571583
align: Align,
572-
) -> InterpResult<'tcx, Option<AllocRef<'a, 'tcx, M::Provenance, M::AllocExtra>>> {
584+
) -> InterpResult<'tcx, Option<AllocRef<'a, 'tcx, M::Provenance, M::AllocExtra, M::Bytes>>> {
573585
let ptr_and_alloc = self.check_and_deref_ptr(
574586
ptr,
575587
size,
@@ -612,7 +624,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
612624
fn get_alloc_raw_mut(
613625
&mut self,
614626
id: AllocId,
615-
) -> InterpResult<'tcx, (&mut Allocation<M::Provenance, M::AllocExtra>, &mut M)> {
627+
) -> InterpResult<'tcx, (&mut Allocation<M::Provenance, M::AllocExtra, M::Bytes>, &mut M)> {
616628
// We have "NLL problem case #3" here, which cannot be worked around without loss of
617629
// efficiency even for the common case where the key is in the map.
618630
// <https://rust-lang.github.io/rfcs/2094-nll.html#problem-case-3-conditional-control-flow-across-functions>
@@ -641,7 +653,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
641653
ptr: Pointer<Option<M::Provenance>>,
642654
size: Size,
643655
align: Align,
644-
) -> InterpResult<'tcx, Option<AllocRefMut<'a, 'tcx, M::Provenance, M::AllocExtra>>> {
656+
) -> InterpResult<'tcx, Option<AllocRefMut<'a, 'tcx, M::Provenance, M::AllocExtra, M::Bytes>>> {
645657
let parts = self.get_ptr_access(ptr, size, align)?;
646658
if let Some((alloc_id, offset, prov)) = parts {
647659
let tcx = *self.tcx;
@@ -840,11 +852,11 @@ pub struct DumpAllocs<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> {
840852
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> std::fmt::Debug for DumpAllocs<'a, 'mir, 'tcx, M> {
841853
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
842854
// Cannot be a closure because it is generic in `Prov`, `Extra`.
843-
fn write_allocation_track_relocs<'tcx, Prov: Provenance, Extra>(
855+
fn write_allocation_track_relocs<'tcx, Prov: Provenance, Extra, Bytes: AllocBytes>(
844856
fmt: &mut std::fmt::Formatter<'_>,
845857
tcx: TyCtxt<'tcx>,
846858
allocs_to_print: &mut VecDeque<AllocId>,
847-
alloc: &Allocation<Prov, Extra>,
859+
alloc: &Allocation<Prov, Extra, Bytes>,
848860
) -> std::fmt::Result {
849861
for alloc_id in alloc.provenance().provenances().filter_map(|prov| prov.get_alloc_id())
850862
{
@@ -912,7 +924,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> std::fmt::Debug for DumpAllocs<'a,
912924
}
913925

914926
/// Reading and writing.
915-
impl<'tcx, 'a, Prov: Provenance, Extra> AllocRefMut<'a, 'tcx, Prov, Extra> {
927+
impl<'tcx, 'a, Prov: Provenance, Extra, Bytes: AllocBytes> AllocRefMut<'a, 'tcx, Prov, Extra, Bytes> {
916928
/// `range` is relative to this allocation reference, not the base of the allocation.
917929
pub fn write_scalar(&mut self, range: AllocRange, val: Scalar<Prov>) -> InterpResult<'tcx> {
918930
let range = self.range.subrange(range);
@@ -937,7 +949,7 @@ impl<'tcx, 'a, Prov: Provenance, Extra> AllocRefMut<'a, 'tcx, Prov, Extra> {
937949
}
938950
}
939951

940-
impl<'tcx, 'a, Prov: Provenance, Extra> AllocRef<'a, 'tcx, Prov, Extra> {
952+
impl<'tcx, 'a, Prov: Provenance, Extra, Bytes: AllocBytes> AllocRef<'a, 'tcx, Prov, Extra, Bytes> {
941953
/// `range` is relative to this allocation reference, not the base of the allocation.
942954
pub fn read_scalar(
943955
&self,

compiler/rustc_const_eval/src/interpret/place.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ where
340340
pub(super) fn get_place_alloc(
341341
&self,
342342
place: &MPlaceTy<'tcx, M::Provenance>,
343-
) -> InterpResult<'tcx, Option<AllocRef<'_, 'tcx, M::Provenance, M::AllocExtra>>> {
343+
) -> InterpResult<'tcx, Option<AllocRef<'_, 'tcx, M::Provenance, M::AllocExtra, M::Bytes>>> {
344344
assert!(place.layout.is_sized());
345345
assert!(!place.meta.has_meta());
346346
let size = place.layout.size;
@@ -351,7 +351,7 @@ where
351351
pub(super) fn get_place_alloc_mut(
352352
&mut self,
353353
place: &MPlaceTy<'tcx, M::Provenance>,
354-
) -> InterpResult<'tcx, Option<AllocRefMut<'_, 'tcx, M::Provenance, M::AllocExtra>>> {
354+
) -> InterpResult<'tcx, Option<AllocRefMut<'_, 'tcx, M::Provenance, M::AllocExtra, M::Bytes>>> {
355355
assert!(place.layout.is_sized());
356356
assert!(!place.meta.has_meta());
357357
let size = place.layout.size;

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

Lines changed: 72 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ mod tests;
88
use std::borrow::Cow;
99
use std::fmt;
1010
use std::hash;
11-
use std::ops::Range;
11+
use std::hash::Hash;
12+
use std::ops::{Deref, DerefMut, Range};
1213
use std::ptr;
1314

1415
use either::{Left, Right};
@@ -29,6 +30,54 @@ use provenance_map::*;
2930

3031
pub use init_mask::{InitChunk, InitChunkIter};
3132

33+
/// Functionality required for the bytes of an `Allocation`.
34+
pub trait AllocBytes:
35+
Clone
36+
+ fmt::Debug
37+
+ Eq
38+
+ PartialEq
39+
+ Hash
40+
+ Deref<Target = [u8]>
41+
+ DerefMut<Target = [u8]>
42+
{
43+
/// Adjust the bytes to the specified alignment -- by default, this is a no-op.
44+
fn adjust_to_align(self, _align: Align) -> Self;
45+
46+
/// Create an `AllocBytes` from a slice of `u8`.
47+
fn from_bytes<'a>(slice: impl Into<Cow<'a, [u8]>>, _align: Align) -> Self;
48+
49+
/// Create a zeroed `AllocBytes` of the specified size and alignment;
50+
/// call the callback error handler if there is an error in allocating the memory.
51+
fn zeroed<'tcx, F: Fn() -> InterpError<'tcx>>(
52+
size: Size,
53+
_align: Align,
54+
handle_alloc_fail: F,
55+
) -> Result<Self, InterpError<'tcx>>;
56+
}
57+
58+
// Default `bytes` for `Allocation` is a `Box<[u8]>`.
59+
impl AllocBytes for Box<[u8]> {
60+
fn adjust_to_align(self, _align: Align) -> Self {
61+
self
62+
}
63+
64+
fn from_bytes<'a>(slice: impl Into<Cow<'a, [u8]>>, _align: Align) -> Self {
65+
Box::<[u8]>::from(slice.into())
66+
}
67+
68+
fn zeroed<'tcx, F: Fn() -> InterpError<'tcx>>(
69+
size: Size,
70+
_align: Align,
71+
handle_alloc_fail: F,
72+
) -> Result<Self, InterpError<'tcx>> {
73+
let bytes = Box::<[u8]>::try_new_zeroed_slice(size.bytes_usize())
74+
.map_err(|_| handle_alloc_fail())?;
75+
// SAFETY: the box was zero-allocated, which is a valid initial value for Box<[u8]>
76+
let bytes = unsafe { bytes.assume_init() };
77+
Ok(bytes)
78+
}
79+
}
80+
3281
/// This type represents an Allocation in the Miri/CTFE core engine.
3382
///
3483
/// Its public API is rather low-level, working directly with allocation offsets and a custom error
@@ -38,10 +87,10 @@ pub use init_mask::{InitChunk, InitChunkIter};
3887
// hashed. (see the `Hash` impl below for more details), so the impl is not derived.
3988
#[derive(Clone, Eq, PartialEq, TyEncodable, TyDecodable)]
4089
#[derive(HashStable)]
41-
pub struct Allocation<Prov: Provenance = AllocId, Extra = ()> {
90+
pub struct Allocation<Prov: Provenance = AllocId, Extra = (), Bytes = Box<[u8]>> {
4291
/// The actual bytes of the allocation.
4392
/// Note that the bytes of a pointer represent the offset of the pointer.
44-
bytes: Box<[u8]>,
93+
bytes: Bytes,
4594
/// Maps from byte addresses to extra provenance data for each pointer.
4695
/// Only the first byte of a pointer is inserted into the map; i.e.,
4796
/// every entry in this map applies to `pointer_size` consecutive bytes starting
@@ -220,14 +269,14 @@ impl AllocRange {
220269
}
221270

222271
// The constructors are all without extra; the extra gets added by a machine hook later.
223-
impl<Prov: Provenance> Allocation<Prov> {
272+
impl<Prov: Provenance, Bytes: AllocBytes> Allocation<Prov, (), Bytes> {
224273
/// Creates an allocation initialized by the given bytes
225274
pub fn from_bytes<'a>(
226275
slice: impl Into<Cow<'a, [u8]>>,
227276
align: Align,
228277
mutability: Mutability,
229278
) -> Self {
230-
let bytes = Box::<[u8]>::from(slice.into());
279+
let bytes = Bytes::from_bytes(slice, align);
231280
let size = Size::from_bytes(bytes.len());
232281
Self {
233282
bytes,
@@ -248,7 +297,7 @@ impl<Prov: Provenance> Allocation<Prov> {
248297
///
249298
/// If `panic_on_fail` is true, this will never return `Err`.
250299
pub fn uninit<'tcx>(size: Size, align: Align, panic_on_fail: bool) -> InterpResult<'tcx, Self> {
251-
let bytes = Box::<[u8]>::try_new_zeroed_slice(size.bytes_usize()).map_err(|_| {
300+
let handle_alloc_fail = || -> InterpError<'tcx> {
252301
// This results in an error that can happen non-deterministically, since the memory
253302
// available to the compiler can change between runs. Normally queries are always
254303
// deterministic. However, we can be non-deterministic here because all uses of const
@@ -261,9 +310,10 @@ impl<Prov: Provenance> Allocation<Prov> {
261310
tcx.sess.delay_span_bug(DUMMY_SP, "exhausted memory during interpretation")
262311
});
263312
InterpError::ResourceExhaustion(ResourceExhaustionInfo::MemoryExhausted)
264-
})?;
265-
// SAFETY: the box was zero-allocated, which is a valid initial value for Box<[u8]>
266-
let bytes = unsafe { bytes.assume_init() };
313+
};
314+
315+
let bytes = Bytes::zeroed(size, align, handle_alloc_fail)?;
316+
267317
Ok(Allocation {
268318
bytes,
269319
provenance: ProvenanceMap::new(),
@@ -275,17 +325,19 @@ impl<Prov: Provenance> Allocation<Prov> {
275325
}
276326
}
277327

278-
impl Allocation {
328+
impl<Bytes: AllocBytes> Allocation<AllocId, (), Bytes> {
279329
/// Adjust allocation from the ones in tcx to a custom Machine instance
280330
/// with a different Provenance and Extra type.
281331
pub fn adjust_from_tcx<Prov: Provenance, Extra, Err>(
282332
self,
283333
cx: &impl HasDataLayout,
284334
extra: Extra,
285335
mut adjust_ptr: impl FnMut(Pointer<AllocId>) -> Result<Pointer<Prov>, Err>,
286-
) -> Result<Allocation<Prov, Extra>, Err> {
287-
// Compute new pointer provenance, which also adjusts the bytes.
288-
let mut bytes = self.bytes;
336+
) -> Result<Allocation<Prov, Extra, Bytes>, Err> {
337+
// Compute new pointer provenance, which also adjusts the bytes, and realign the pointer if
338+
// necessary.
339+
let mut bytes = self.bytes.adjust_to_align(self.align);
340+
289341
let mut new_provenance = Vec::with_capacity(self.provenance.ptrs().len());
290342
let ptr_size = cx.data_layout().pointer_size.bytes_usize();
291343
let endian = cx.data_layout().endian;
@@ -311,7 +363,7 @@ impl Allocation {
311363
}
312364

313365
/// Raw accessors. Provide access to otherwise private bytes.
314-
impl<Prov: Provenance, Extra> Allocation<Prov, Extra> {
366+
impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes> {
315367
pub fn len(&self) -> usize {
316368
self.bytes.len()
317369
}
@@ -340,7 +392,11 @@ impl<Prov: Provenance, Extra> Allocation<Prov, Extra> {
340392
}
341393

342394
/// Byte accessors.
343-
impl<Prov: Provenance, Extra> Allocation<Prov, Extra> {
395+
impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes> {
396+
pub fn base_addr(&self) -> *const u8 {
397+
self.bytes.as_ptr()
398+
}
399+
344400
/// This is the entirely abstraction-violating way to just grab the raw bytes without
345401
/// caring about provenance or initialization.
346402
///
@@ -412,7 +468,7 @@ impl<Prov: Provenance, Extra> Allocation<Prov, Extra> {
412468
}
413469

414470
/// Reading and writing.
415-
impl<Prov: Provenance, Extra> Allocation<Prov, Extra> {
471+
impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes> {
416472
/// Sets the init bit for the given range.
417473
fn mark_init(&mut self, range: AllocRange, is_init: bool) {
418474
if range.size.bytes() == 0 {

compiler/rustc_middle/src/mir/interpret/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ pub use self::error::{
127127
pub use self::value::{get_slice_bytes, ConstAlloc, ConstValue, Scalar};
128128

129129
pub use self::allocation::{
130-
alloc_range, AllocError, AllocRange, AllocResult, Allocation, ConstAllocation, InitChunk,
130+
alloc_range, AllocBytes, AllocError, AllocRange, AllocResult, Allocation, ConstAllocation, InitChunk,
131131
InitChunkIter,
132132
};
133133

0 commit comments

Comments
 (0)