mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-23 05:03:47 +00:00
interpret: Immediate::offset: use shared sanity-check function to ensure invariant
This commit is contained in:
parent
fd1f8aa05d
commit
a8f9a32650
@ -113,28 +113,47 @@ impl<Prov: Provenance> Immediate<Prov> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Assert that this immediate is a valid value for the given ABI.
|
/// Assert that this immediate is a valid value for the given ABI.
|
||||||
pub fn assert_matches_abi(self, abi: Abi, cx: &impl HasDataLayout) {
|
pub fn assert_matches_abi(self, abi: Abi, msg: &str, cx: &impl HasDataLayout) {
|
||||||
match (self, abi) {
|
match (self, abi) {
|
||||||
(Immediate::Scalar(scalar), Abi::Scalar(s)) => {
|
(Immediate::Scalar(scalar), Abi::Scalar(s)) => {
|
||||||
assert_eq!(scalar.size(), s.size(cx));
|
assert_eq!(scalar.size(), s.size(cx), "{msg}: scalar value has wrong size");
|
||||||
if !matches!(s.primitive(), abi::Pointer(..)) {
|
if !matches!(s.primitive(), abi::Pointer(..)) {
|
||||||
// This is not a pointer, it should not carry provenance.
|
// This is not a pointer, it should not carry provenance.
|
||||||
assert!(matches!(scalar, Scalar::Int(..)));
|
assert!(
|
||||||
|
matches!(scalar, Scalar::Int(..)),
|
||||||
|
"{msg}: scalar value should be an integer, but has provenance"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(Immediate::ScalarPair(a_val, b_val), Abi::ScalarPair(a, b)) => {
|
(Immediate::ScalarPair(a_val, b_val), Abi::ScalarPair(a, b)) => {
|
||||||
assert_eq!(a_val.size(), a.size(cx));
|
assert_eq!(
|
||||||
|
a_val.size(),
|
||||||
|
a.size(cx),
|
||||||
|
"{msg}: first component of scalar pair has wrong size"
|
||||||
|
);
|
||||||
if !matches!(a.primitive(), abi::Pointer(..)) {
|
if !matches!(a.primitive(), abi::Pointer(..)) {
|
||||||
assert!(matches!(a_val, Scalar::Int(..)));
|
assert!(
|
||||||
|
matches!(a_val, Scalar::Int(..)),
|
||||||
|
"{msg}: first component of scalar pair should be an integer, but has provenance"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
assert_eq!(b_val.size(), b.size(cx));
|
assert_eq!(
|
||||||
|
b_val.size(),
|
||||||
|
b.size(cx),
|
||||||
|
"{msg}: second component of scalar pair has wrong size"
|
||||||
|
);
|
||||||
if !matches!(b.primitive(), abi::Pointer(..)) {
|
if !matches!(b.primitive(), abi::Pointer(..)) {
|
||||||
assert!(matches!(b_val, Scalar::Int(..)));
|
assert!(
|
||||||
|
matches!(b_val, Scalar::Int(..)),
|
||||||
|
"{msg}: second component of scalar pair should be an integer, but has provenance"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(Immediate::Uninit, _) => {}
|
(Immediate::Uninit, _) => {
|
||||||
|
assert!(abi.is_sized(), "{msg}: unsized immediates are not a thing");
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
bug!("value {self:?} does not match ABI {abi:?})",)
|
bug!("{msg}: value {self:?} does not match ABI {abi:?})",)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -241,6 +260,7 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn from_immediate(imm: Immediate<Prov>, layout: TyAndLayout<'tcx>) -> Self {
|
pub fn from_immediate(imm: Immediate<Prov>, layout: TyAndLayout<'tcx>) -> Self {
|
||||||
|
// Without a `cx` we cannot call `assert_matches_abi`.
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
match (imm, layout.abi) {
|
match (imm, layout.abi) {
|
||||||
(Immediate::Scalar(..), Abi::Scalar(..)) => true,
|
(Immediate::Scalar(..), Abi::Scalar(..)) => true,
|
||||||
@ -261,7 +281,6 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
|
|||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn from_scalar_int(s: ScalarInt, layout: TyAndLayout<'tcx>) -> Self {
|
pub fn from_scalar_int(s: ScalarInt, layout: TyAndLayout<'tcx>) -> Self {
|
||||||
assert_eq!(s.size(), layout.size);
|
|
||||||
Self::from_scalar(Scalar::from(s), layout)
|
Self::from_scalar(Scalar::from(s), layout)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -334,7 +353,10 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
|
|||||||
/// given layout.
|
/// given layout.
|
||||||
// Not called `offset` to avoid confusion with the trait method.
|
// Not called `offset` to avoid confusion with the trait method.
|
||||||
fn offset_(&self, offset: Size, layout: TyAndLayout<'tcx>, cx: &impl HasDataLayout) -> Self {
|
fn offset_(&self, offset: Size, layout: TyAndLayout<'tcx>, cx: &impl HasDataLayout) -> Self {
|
||||||
debug_assert!(layout.is_sized(), "unsized immediates are not a thing");
|
// Verify that the input matches its type.
|
||||||
|
if cfg!(debug_assertions) {
|
||||||
|
self.assert_matches_abi(self.layout.abi, "invalid input to Immediate::offset", cx);
|
||||||
|
}
|
||||||
// `ImmTy` have already been checked to be in-bounds, so we can just check directly if this
|
// `ImmTy` have already been checked to be in-bounds, so we can just check directly if this
|
||||||
// remains in-bounds. This cannot actually be violated since projections are type-checked
|
// remains in-bounds. This cannot actually be violated since projections are type-checked
|
||||||
// and bounds-checked.
|
// and bounds-checked.
|
||||||
@ -368,32 +390,14 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
|
|||||||
// the field covers the entire type
|
// the field covers the entire type
|
||||||
_ if layout.size == self.layout.size => {
|
_ if layout.size == self.layout.size => {
|
||||||
assert_eq!(offset.bytes(), 0);
|
assert_eq!(offset.bytes(), 0);
|
||||||
assert!(
|
|
||||||
match (self.layout.abi, layout.abi) {
|
|
||||||
(Abi::Scalar(l), Abi::Scalar(r)) => l.size(cx) == r.size(cx),
|
|
||||||
(Abi::ScalarPair(l1, l2), Abi::ScalarPair(r1, r2)) =>
|
|
||||||
l1.size(cx) == r1.size(cx) && l2.size(cx) == r2.size(cx),
|
|
||||||
_ => false,
|
|
||||||
},
|
|
||||||
"cannot project into {} immediate with equally-sized field {}\nouter ABI: {:#?}\nfield ABI: {:#?}",
|
|
||||||
self.layout.ty,
|
|
||||||
layout.ty,
|
|
||||||
self.layout.abi,
|
|
||||||
layout.abi,
|
|
||||||
);
|
|
||||||
**self
|
**self
|
||||||
}
|
}
|
||||||
// extract fields from types with `ScalarPair` ABI
|
// extract fields from types with `ScalarPair` ABI
|
||||||
(Immediate::ScalarPair(a_val, b_val), Abi::ScalarPair(a, b)) => {
|
(Immediate::ScalarPair(a_val, b_val), Abi::ScalarPair(a, b)) => {
|
||||||
assert_matches!(layout.abi, Abi::Scalar(..));
|
|
||||||
Immediate::from(if offset.bytes() == 0 {
|
Immediate::from(if offset.bytes() == 0 {
|
||||||
// It is "okay" to transmute from `usize` to a pointer (GVN relies on that).
|
|
||||||
// So only compare the size.
|
|
||||||
assert_eq!(layout.size, a.size(cx));
|
|
||||||
a_val
|
a_val
|
||||||
} else {
|
} else {
|
||||||
assert_eq!(offset, a.size(cx).align_to(b.align(cx).abi));
|
assert_eq!(offset, a.size(cx).align_to(b.align(cx).abi));
|
||||||
assert_eq!(layout.size, b.size(cx));
|
|
||||||
b_val
|
b_val
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -405,6 +409,8 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> {
|
|||||||
self.layout
|
self.layout
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
// Ensure the new layout matches the new value.
|
||||||
|
inner_val.assert_matches_abi(layout.abi, "invalid field type in Immediate::offset", cx);
|
||||||
|
|
||||||
ImmTy::from_immediate(inner_val, layout)
|
ImmTy::from_immediate(inner_val, layout)
|
||||||
}
|
}
|
||||||
|
@ -658,7 +658,11 @@ where
|
|||||||
// Things can ge wrong in quite weird ways when this is violated.
|
// Things can ge wrong in quite weird ways when this is violated.
|
||||||
// Unfortunately this is too expensive to do in release builds.
|
// Unfortunately this is too expensive to do in release builds.
|
||||||
if cfg!(debug_assertions) {
|
if cfg!(debug_assertions) {
|
||||||
src.assert_matches_abi(local_layout.abi, self);
|
src.assert_matches_abi(
|
||||||
|
local_layout.abi,
|
||||||
|
"invalid immediate for given destination place",
|
||||||
|
self,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Left(mplace) => {
|
Left(mplace) => {
|
||||||
@ -679,7 +683,7 @@ where
|
|||||||
) -> InterpResult<'tcx> {
|
) -> InterpResult<'tcx> {
|
||||||
// We use the sizes from `value` below.
|
// We use the sizes from `value` below.
|
||||||
// Ensure that matches the type of the place it is written to.
|
// Ensure that matches the type of the place it is written to.
|
||||||
value.assert_matches_abi(layout.abi, self);
|
value.assert_matches_abi(layout.abi, "invalid immediate for given destination place", self);
|
||||||
// Note that it is really important that the type here is the right one, and matches the
|
// Note that it is really important that the type here is the right one, and matches the
|
||||||
// type things are read at. In case `value` is a `ScalarPair`, we don't do any magic here
|
// type things are read at. In case `value` is a `ScalarPair`, we don't do any magic here
|
||||||
// to handle padding properly, which is only correct if we never look at this data with the
|
// to handle padding properly, which is only correct if we never look at this data with the
|
||||||
|
@ -82,6 +82,10 @@ pub trait Projectable<'tcx, Prov: Provenance>: Sized + std::fmt::Debug {
|
|||||||
self.offset_with_meta(offset, OffsetMode::Inbounds, MemPlaceMeta::None, layout, ecx)
|
self.offset_with_meta(offset, OffsetMode::Inbounds, MemPlaceMeta::None, layout, ecx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This does an offset-by-zero, which is effectively a transmute. Note however that
|
||||||
|
/// not all transmutes are supported by all projectables -- specifically, if this is an
|
||||||
|
/// `OpTy` or `ImmTy`, the new layout must have almost the same ABI as the old one
|
||||||
|
/// (only changing the `valid_range` is allowed and turning integers into pointers).
|
||||||
fn transmute<M: Machine<'tcx, Provenance = Prov>>(
|
fn transmute<M: Machine<'tcx, Provenance = Prov>>(
|
||||||
&self,
|
&self,
|
||||||
layout: TyAndLayout<'tcx>,
|
layout: TyAndLayout<'tcx>,
|
||||||
|
Loading…
Reference in New Issue
Block a user