avoid re-checking the offset while iterating an array/slice
This commit is contained in:
parent
b1ebf002c3
commit
ea9a24e32e
@ -26,7 +26,7 @@ pub use self::machine::{compile_time_machine, AllocMap, Machine, MayLeak, StackP
|
|||||||
pub use self::memory::{AllocKind, AllocRef, AllocRefMut, FnVal, Memory, MemoryKind};
|
pub use self::memory::{AllocKind, AllocRef, AllocRefMut, FnVal, Memory, MemoryKind};
|
||||||
pub use self::operand::{ImmTy, Immediate, OpTy, Readable};
|
pub use self::operand::{ImmTy, Immediate, OpTy, Readable};
|
||||||
pub use self::place::{MPlaceTy, MemPlaceMeta, PlaceTy, Writeable};
|
pub use self::place::{MPlaceTy, MemPlaceMeta, PlaceTy, Writeable};
|
||||||
pub use self::projection::Projectable;
|
pub use self::projection::{OffsetMode, Projectable};
|
||||||
pub use self::terminator::FnArg;
|
pub use self::terminator::FnArg;
|
||||||
pub use self::validity::{CtfeValidationMode, RefTracking};
|
pub use self::validity::{CtfeValidationMode, RefTracking};
|
||||||
pub use self::visitor::ValueVisitor;
|
pub use self::visitor::ValueVisitor;
|
||||||
|
@ -14,7 +14,8 @@ use rustc_target::abi::{self, Abi, Align, HasDataLayout, Size};
|
|||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
alloc_range, from_known_layout, mir_assign_valid_types, AllocId, Frame, InterpCx, InterpResult,
|
alloc_range, from_known_layout, mir_assign_valid_types, AllocId, Frame, InterpCx, InterpResult,
|
||||||
MPlaceTy, Machine, MemPlace, MemPlaceMeta, PlaceTy, Pointer, Projectable, Provenance, Scalar,
|
MPlaceTy, Machine, MemPlace, MemPlaceMeta, OffsetMode, PlaceTy, Pointer, Projectable,
|
||||||
|
Provenance, Scalar,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// An `Immediate` represents a single immediate self-contained Rust value.
|
/// An `Immediate` represents a single immediate self-contained Rust value.
|
||||||
@ -297,6 +298,7 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for ImmTy<'tcx, Prov> {
|
|||||||
fn offset_with_meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
fn offset_with_meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||||
&self,
|
&self,
|
||||||
offset: Size,
|
offset: Size,
|
||||||
|
_mode: OffsetMode,
|
||||||
meta: MemPlaceMeta<Prov>,
|
meta: MemPlaceMeta<Prov>,
|
||||||
layout: TyAndLayout<'tcx>,
|
layout: TyAndLayout<'tcx>,
|
||||||
ecx: &InterpCx<'mir, 'tcx, M>,
|
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||||
@ -391,12 +393,13 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for OpTy<'tcx, Prov> {
|
|||||||
fn offset_with_meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
fn offset_with_meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||||
&self,
|
&self,
|
||||||
offset: Size,
|
offset: Size,
|
||||||
|
mode: OffsetMode,
|
||||||
meta: MemPlaceMeta<Prov>,
|
meta: MemPlaceMeta<Prov>,
|
||||||
layout: TyAndLayout<'tcx>,
|
layout: TyAndLayout<'tcx>,
|
||||||
ecx: &InterpCx<'mir, 'tcx, M>,
|
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||||
) -> InterpResult<'tcx, Self> {
|
) -> InterpResult<'tcx, Self> {
|
||||||
match self.as_mplace_or_imm() {
|
match self.as_mplace_or_imm() {
|
||||||
Left(mplace) => Ok(mplace.offset_with_meta(offset, meta, layout, ecx)?.into()),
|
Left(mplace) => Ok(mplace.offset_with_meta(offset, mode, meta, layout, ecx)?.into()),
|
||||||
Right(imm) => {
|
Right(imm) => {
|
||||||
assert_matches!(meta, MemPlaceMeta::None); // no place to store metadata here
|
assert_matches!(meta, MemPlaceMeta::None); // no place to store metadata here
|
||||||
// Every part of an uninit is uninit.
|
// Every part of an uninit is uninit.
|
||||||
|
@ -16,8 +16,8 @@ use rustc_target::abi::{Abi, Align, FieldIdx, HasDataLayout, Size, FIRST_VARIANT
|
|||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
alloc_range, mir_assign_valid_types, AllocId, AllocRef, AllocRefMut, ImmTy, Immediate,
|
alloc_range, mir_assign_valid_types, AllocId, AllocRef, AllocRefMut, ImmTy, Immediate,
|
||||||
InterpCx, InterpResult, Machine, MemoryKind, OpTy, Operand, Pointer, PointerArithmetic,
|
InterpCx, InterpResult, Machine, MemoryKind, OffsetMode, OpTy, Operand, Pointer,
|
||||||
Projectable, Provenance, Readable, Scalar,
|
PointerArithmetic, Projectable, Provenance, Readable, Scalar,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
|
#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
|
||||||
@ -91,6 +91,7 @@ impl<Prov: Provenance> MemPlace<Prov> {
|
|||||||
fn offset_with_meta_<'mir, 'tcx, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
fn offset_with_meta_<'mir, 'tcx, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||||
self,
|
self,
|
||||||
offset: Size,
|
offset: Size,
|
||||||
|
mode: OffsetMode,
|
||||||
meta: MemPlaceMeta<Prov>,
|
meta: MemPlaceMeta<Prov>,
|
||||||
ecx: &InterpCx<'mir, 'tcx, M>,
|
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||||
) -> InterpResult<'tcx, Self> {
|
) -> InterpResult<'tcx, Self> {
|
||||||
@ -101,8 +102,12 @@ impl<Prov: Provenance> MemPlace<Prov> {
|
|||||||
if offset > ecx.data_layout().max_size_of_val() {
|
if offset > ecx.data_layout().max_size_of_val() {
|
||||||
throw_ub!(PointerArithOverflow);
|
throw_ub!(PointerArithOverflow);
|
||||||
}
|
}
|
||||||
let offset: i64 = offset.bytes().try_into().unwrap();
|
let ptr = match mode {
|
||||||
let ptr = ecx.ptr_offset_inbounds(self.ptr, offset)?;
|
OffsetMode::Inbounds => {
|
||||||
|
ecx.ptr_offset_inbounds(self.ptr, offset.bytes().try_into().unwrap())?
|
||||||
|
}
|
||||||
|
OffsetMode::Wrapping => self.ptr.wrapping_offset(offset, ecx),
|
||||||
|
};
|
||||||
Ok(MemPlace { ptr, meta })
|
Ok(MemPlace { ptr, meta })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -194,12 +199,13 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for MPlaceTy<'tcx, Prov> {
|
|||||||
fn offset_with_meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
fn offset_with_meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||||
&self,
|
&self,
|
||||||
offset: Size,
|
offset: Size,
|
||||||
|
mode: OffsetMode,
|
||||||
meta: MemPlaceMeta<Prov>,
|
meta: MemPlaceMeta<Prov>,
|
||||||
layout: TyAndLayout<'tcx>,
|
layout: TyAndLayout<'tcx>,
|
||||||
ecx: &InterpCx<'mir, 'tcx, M>,
|
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||||
) -> InterpResult<'tcx, Self> {
|
) -> InterpResult<'tcx, Self> {
|
||||||
Ok(MPlaceTy {
|
Ok(MPlaceTy {
|
||||||
mplace: self.mplace.offset_with_meta_(offset, meta, ecx)?,
|
mplace: self.mplace.offset_with_meta_(offset, mode, meta, ecx)?,
|
||||||
align: self.align.restrict_for_offset(offset),
|
align: self.align.restrict_for_offset(offset),
|
||||||
layout,
|
layout,
|
||||||
})
|
})
|
||||||
@ -306,12 +312,13 @@ impl<'tcx, Prov: Provenance> Projectable<'tcx, Prov> for PlaceTy<'tcx, Prov> {
|
|||||||
fn offset_with_meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
fn offset_with_meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||||
&self,
|
&self,
|
||||||
offset: Size,
|
offset: Size,
|
||||||
|
mode: OffsetMode,
|
||||||
meta: MemPlaceMeta<Prov>,
|
meta: MemPlaceMeta<Prov>,
|
||||||
layout: TyAndLayout<'tcx>,
|
layout: TyAndLayout<'tcx>,
|
||||||
ecx: &InterpCx<'mir, 'tcx, M>,
|
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||||
) -> InterpResult<'tcx, Self> {
|
) -> InterpResult<'tcx, Self> {
|
||||||
Ok(match self.as_mplace_or_local() {
|
Ok(match self.as_mplace_or_local() {
|
||||||
Left(mplace) => mplace.offset_with_meta(offset, meta, layout, ecx)?.into(),
|
Left(mplace) => mplace.offset_with_meta(offset, mode, meta, layout, ecx)?.into(),
|
||||||
Right((frame, local, old_offset)) => {
|
Right((frame, local, old_offset)) => {
|
||||||
debug_assert!(layout.is_sized(), "unsized locals should live in memory");
|
debug_assert!(layout.is_sized(), "unsized locals should live in memory");
|
||||||
assert_matches!(meta, MemPlaceMeta::None); // we couldn't store it anyway...
|
assert_matches!(meta, MemPlaceMeta::None); // we couldn't store it anyway...
|
||||||
@ -952,7 +959,13 @@ where
|
|||||||
&mut Operand::Indirect(mplace) => mplace, // this already was an indirect local
|
&mut Operand::Indirect(mplace) => mplace, // this already was an indirect local
|
||||||
};
|
};
|
||||||
if let Some(offset) = offset {
|
if let Some(offset) = offset {
|
||||||
whole_local.offset_with_meta_(offset, MemPlaceMeta::None, self)?
|
// This offset is always inbounds, no need to check it again.
|
||||||
|
whole_local.offset_with_meta_(
|
||||||
|
offset,
|
||||||
|
OffsetMode::Wrapping,
|
||||||
|
MemPlaceMeta::None,
|
||||||
|
self,
|
||||||
|
)?
|
||||||
} else {
|
} else {
|
||||||
// Preserve wide place metadata, do not call `offset`.
|
// Preserve wide place metadata, do not call `offset`.
|
||||||
whole_local
|
whole_local
|
||||||
|
@ -19,6 +19,15 @@ use rustc_target::abi::{self, VariantIdx};
|
|||||||
|
|
||||||
use super::{InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, Provenance, Scalar};
|
use super::{InterpCx, InterpResult, MPlaceTy, Machine, MemPlaceMeta, OpTy, Provenance, Scalar};
|
||||||
|
|
||||||
|
/// Describes the constraints placed on offset-projections.
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub enum OffsetMode {
|
||||||
|
/// The offset has to be inbounds, like `ptr::offset`.
|
||||||
|
Inbounds,
|
||||||
|
/// No constraints, just wrap around the edge of the address space.
|
||||||
|
Wrapping,
|
||||||
|
}
|
||||||
|
|
||||||
/// A thing that we can project into, and that has a layout.
|
/// A thing that we can project into, and that has a layout.
|
||||||
pub trait Projectable<'tcx, Prov: Provenance>: Sized + std::fmt::Debug {
|
pub trait Projectable<'tcx, Prov: Provenance>: Sized + std::fmt::Debug {
|
||||||
/// Get the layout.
|
/// Get the layout.
|
||||||
@ -53,6 +62,7 @@ pub trait Projectable<'tcx, Prov: Provenance>: Sized + std::fmt::Debug {
|
|||||||
fn offset_with_meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
fn offset_with_meta<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||||
&self,
|
&self,
|
||||||
offset: Size,
|
offset: Size,
|
||||||
|
mode: OffsetMode,
|
||||||
meta: MemPlaceMeta<Prov>,
|
meta: MemPlaceMeta<Prov>,
|
||||||
layout: TyAndLayout<'tcx>,
|
layout: TyAndLayout<'tcx>,
|
||||||
ecx: &InterpCx<'mir, 'tcx, M>,
|
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||||
@ -65,7 +75,7 @@ pub trait Projectable<'tcx, Prov: Provenance>: Sized + std::fmt::Debug {
|
|||||||
ecx: &InterpCx<'mir, 'tcx, M>,
|
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||||
) -> InterpResult<'tcx, Self> {
|
) -> InterpResult<'tcx, Self> {
|
||||||
assert!(layout.is_sized());
|
assert!(layout.is_sized());
|
||||||
self.offset_with_meta(offset, MemPlaceMeta::None, layout, ecx)
|
self.offset_with_meta(offset, OffsetMode::Inbounds, MemPlaceMeta::None, layout, ecx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transmute<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
fn transmute<'mir, M: Machine<'mir, 'tcx, Provenance = Prov>>(
|
||||||
@ -75,7 +85,7 @@ pub trait Projectable<'tcx, Prov: Provenance>: Sized + std::fmt::Debug {
|
|||||||
) -> InterpResult<'tcx, Self> {
|
) -> InterpResult<'tcx, Self> {
|
||||||
assert!(self.layout().is_sized() && layout.is_sized());
|
assert!(self.layout().is_sized() && layout.is_sized());
|
||||||
assert_eq!(self.layout().size, layout.size);
|
assert_eq!(self.layout().size, layout.size);
|
||||||
self.offset_with_meta(Size::ZERO, MemPlaceMeta::None, layout, ecx)
|
self.offset_with_meta(Size::ZERO, OffsetMode::Wrapping, MemPlaceMeta::None, layout, ecx)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert this to an `OpTy`. This might be an irreversible transformation, but is useful for
|
/// Convert this to an `OpTy`. This might be an irreversible transformation, but is useful for
|
||||||
@ -102,7 +112,17 @@ impl<'tcx, 'a, Prov: Provenance, P: Projectable<'tcx, Prov>> ArrayIterator<'tcx,
|
|||||||
ecx: &InterpCx<'mir, 'tcx, M>,
|
ecx: &InterpCx<'mir, 'tcx, M>,
|
||||||
) -> InterpResult<'tcx, Option<(u64, P)>> {
|
) -> InterpResult<'tcx, Option<(u64, P)>> {
|
||||||
let Some(idx) = self.range.next() else { return Ok(None) };
|
let Some(idx) = self.range.next() else { return Ok(None) };
|
||||||
Ok(Some((idx, self.base.offset(self.stride * idx, self.field_layout, ecx)?)))
|
// We use `Wrapping` here since the offset has already been checked when the iterator was created.
|
||||||
|
Ok(Some((
|
||||||
|
idx,
|
||||||
|
self.base.offset_with_meta(
|
||||||
|
self.stride * idx,
|
||||||
|
OffsetMode::Wrapping,
|
||||||
|
MemPlaceMeta::None,
|
||||||
|
self.field_layout,
|
||||||
|
ecx,
|
||||||
|
)?,
|
||||||
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,7 +177,7 @@ where
|
|||||||
(MemPlaceMeta::None, offset)
|
(MemPlaceMeta::None, offset)
|
||||||
};
|
};
|
||||||
|
|
||||||
base.offset_with_meta(offset, meta, field_layout, self)
|
base.offset_with_meta(offset, OffsetMode::Inbounds, meta, field_layout, self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Downcasting to an enum variant.
|
/// Downcasting to an enum variant.
|
||||||
@ -246,6 +266,9 @@ where
|
|||||||
};
|
};
|
||||||
let len = base.len(self)?;
|
let len = base.len(self)?;
|
||||||
let field_layout = base.layout().field(self, 0);
|
let field_layout = base.layout().field(self, 0);
|
||||||
|
// Ensure that all the offsets are in-bounds once, up-front.
|
||||||
|
base.offset(len * stride, self.layout_of(self.tcx.types.unit).unwrap(), self)?;
|
||||||
|
// Create the iterator.
|
||||||
Ok(ArrayIterator { base, range: 0..len, stride, field_layout, _phantom: PhantomData })
|
Ok(ArrayIterator { base, range: 0..len, stride, field_layout, _phantom: PhantomData })
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -303,7 +326,7 @@ where
|
|||||||
};
|
};
|
||||||
let layout = self.layout_of(ty)?;
|
let layout = self.layout_of(ty)?;
|
||||||
|
|
||||||
base.offset_with_meta(from_offset, meta, layout, self)
|
base.offset_with_meta(from_offset, OffsetMode::Inbounds, meta, layout, self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Applying a general projection
|
/// Applying a general projection
|
||||||
|
@ -88,4 +88,7 @@ const PARTIAL_POINTER: () = unsafe {
|
|||||||
const VALID_ENUM1: E = { let e = E::A; e };
|
const VALID_ENUM1: E = { let e = E::A; e };
|
||||||
const VALID_ENUM2: Result<&'static [u8], ()> = { let e = Err(()); e };
|
const VALID_ENUM2: Result<&'static [u8], ()> = { let e = Err(()); e };
|
||||||
|
|
||||||
|
// Htting the (non-integer) array code in validation with an immediate local.
|
||||||
|
const VALID_ARRAY: [Option<i32>; 0] = { let e = [None; 0]; e };
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user