Skip to content

Commit

Permalink
Auto merge of rust-lang#118640 - RalfJung:miri, r=RalfJung
Browse files Browse the repository at this point in the history
Miri subtree update

needed to fix some errors in miri-test-libstd
  • Loading branch information
bors committed Dec 5, 2023
2 parents f67523d + 73db2e4 commit f536185
Show file tree
Hide file tree
Showing 13 changed files with 185 additions and 107 deletions.
2 changes: 1 addition & 1 deletion src/tools/miri/rust-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
225e36cff9809948d6567ab16f75d7b087ea83a7
317d14a56cb8c748bf0e2f2afff89c2249ab4423
8 changes: 8 additions & 0 deletions src/tools/miri/src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1188,3 +1188,11 @@ pub(crate) fn simd_element_to_bool(elem: ImmTy<'_, Provenance>) -> InterpResult<
_ => throw_ub_format!("each element of a SIMD mask must be all-0-bits or all-1-bits"),
})
}

// This looks like something that would be nice to have in the standard library...
pub(crate) fn round_to_next_multiple_of(x: u64, divisor: u64) -> u64 {
assert_ne!(divisor, 0);
// divisor is nonzero; multiplication cannot overflow since we just divided
#[allow(clippy::arithmetic_side_effects)]
return (x.checked_add(divisor - 1).unwrap() / divisor) * divisor;
}
5 changes: 0 additions & 5 deletions src/tools/miri/src/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -777,11 +777,6 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
drop(self.profiler.take());
}

pub(crate) fn round_up_to_multiple_of_page_size(&self, length: u64) -> Option<u64> {
#[allow(clippy::arithmetic_side_effects)] // page size is nonzero
(length.checked_add(self.page_size - 1)? / self.page_size).checked_mul(self.page_size)
}

pub(crate) fn page_align(&self) -> Align {
Align::from_bytes(self.page_size).unwrap()
}
Expand Down
18 changes: 14 additions & 4 deletions src/tools/miri/src/shims/foreign_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -558,16 +558,26 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {

// Promises that a pointer has a given symbolic alignment.
"miri_promise_symbolic_alignment" => {
use rustc_target::abi::AlignFromBytesError;

let [ptr, align] = this.check_shim(abi, Abi::Rust, link_name, args)?;
let ptr = this.read_pointer(ptr)?;
let align = this.read_target_usize(align)?;
let Ok(align) = Align::from_bytes(align) else {
if !align.is_power_of_two() {
throw_unsup_format!(
"`miri_promise_symbolic_alignment`: alignment must be a power of 2"
"`miri_promise_symbolic_alignment`: alignment must be a power of 2, got {align}"
);
};
}
let align = Align::from_bytes(align).unwrap_or_else(|err| {
match err {
AlignFromBytesError::NotPowerOfTwo(_) => unreachable!(),
// When the alignment is a power of 2 but too big, clamp it to MAX.
AlignFromBytesError::TooLarge(_) => Align::MAX,
}
});
let (_, addr) = ptr.into_parts(); // we know the offset is absolute
if addr.bytes() % align.bytes() != 0 {
// Cannot panic since `align` is a power of 2 and hence non-zero.
if addr.bytes().checked_rem(align.bytes()).unwrap() != 0 {
throw_unsup_format!(
"`miri_promise_symbolic_alignment`: pointer is not actually aligned"
);
Expand Down
107 changes: 50 additions & 57 deletions src/tools/miri/src/shims/intrinsics/simd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ use rustc_middle::{mir, ty, ty::FloatTy};
use rustc_span::{sym, Symbol};
use rustc_target::abi::{Endian, HasDataLayout};

use crate::helpers::{
bool_to_simd_element, check_arg_count, round_to_next_multiple_of, simd_element_to_bool,
};
use crate::*;
use helpers::{bool_to_simd_element, check_arg_count, simd_element_to_bool};

impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
Expand Down Expand Up @@ -113,18 +115,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
}
}
Op::Numeric(name) => {
assert!(op.layout.ty.is_integral());
let size = op.layout.size;
let bits = op.to_scalar().to_bits(size).unwrap();
let extra = 128u128.checked_sub(u128::from(size.bits())).unwrap();
let bits_out = match name {
sym::ctlz => u128::from(bits.leading_zeros()).checked_sub(extra).unwrap(),
sym::cttz => u128::from((bits << extra).trailing_zeros()).checked_sub(extra).unwrap(),
sym::bswap => (bits << extra).swap_bytes(),
sym::bitreverse => (bits << extra).reverse_bits(),
_ => unreachable!(),
};
Scalar::from_uint(bits_out, size)
this.numeric_intrinsic(name, op.to_scalar(), op.layout)?
}
};
this.write_scalar(val, &dest)?;
Expand Down Expand Up @@ -405,37 +396,35 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
this.write_immediate(*val, &dest)?;
}
}
// Variant of `select` that takes a bitmask rather than a "vector of bool".
"select_bitmask" => {
let [mask, yes, no] = check_arg_count(args)?;
let (yes, yes_len) = this.operand_to_simd(yes)?;
let (no, no_len) = this.operand_to_simd(no)?;
let (dest, dest_len) = this.place_to_simd(dest)?;
let bitmask_len = dest_len.max(8);
let bitmask_len = round_to_next_multiple_of(dest_len, 8);

// The mask must be an integer or an array.
assert!(
mask.layout.ty.is_integral()
|| matches!(mask.layout.ty.kind(), ty::Array(elemty, _) if elemty == &this.tcx.types.u8)
);
assert!(bitmask_len <= 64);
assert_eq!(bitmask_len, mask.layout.size.bits());
assert_eq!(dest_len, yes_len);
assert_eq!(dest_len, no_len);
let dest_len = u32::try_from(dest_len).unwrap();
let bitmask_len = u32::try_from(bitmask_len).unwrap();

// The mask can be a single integer or an array.
let mask: u64 = match mask.layout.ty.kind() {
ty::Int(..) | ty::Uint(..) =>
this.read_scalar(mask)?.to_bits(mask.layout.size)?.try_into().unwrap(),
ty::Array(elem, _) if matches!(elem.kind(), ty::Uint(ty::UintTy::U8)) => {
let mask_ty = this.machine.layouts.uint(mask.layout.size).unwrap();
let mask = mask.transmute(mask_ty, this)?;
this.read_scalar(&mask)?.to_bits(mask_ty.size)?.try_into().unwrap()
}
_ => bug!("simd_select_bitmask: invalid mask type {}", mask.layout.ty),
};
// To read the mask, we transmute it to an integer.
// That does the right thing wrt endianess.
let mask_ty = this.machine.layouts.uint(mask.layout.size).unwrap();
let mask = mask.transmute(mask_ty, this)?;
let mask: u64 = this.read_scalar(&mask)?.to_bits(mask_ty.size)?.try_into().unwrap();

for i in 0..dest_len {
let mask = mask
& 1u64
.checked_shl(simd_bitmask_index(i, dest_len, this.data_layout().endian))
.unwrap();
let bit_i = simd_bitmask_index(i, dest_len, this.data_layout().endian);
let mask = mask & 1u64.checked_shl(bit_i).unwrap();
let yes = this.read_immediate(&this.project_index(&yes, i.into())?)?;
let no = this.read_immediate(&this.project_index(&no, i.into())?)?;
let dest = this.project_index(&dest, i.into())?;
Expand All @@ -445,6 +434,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
}
for i in dest_len..bitmask_len {
// If the mask is "padded", ensure that padding is all-zero.
// This deliberately does not use `simd_bitmask_index`; these bits are outside
// the bitmask. It does not matter in which order we check them.
let mask = mask & 1u64.checked_shl(i).unwrap();
if mask != 0 {
throw_ub_format!(
Expand All @@ -453,6 +444,36 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
}
}
}
// Converts a "vector of bool" into a bitmask.
"bitmask" => {
let [op] = check_arg_count(args)?;
let (op, op_len) = this.operand_to_simd(op)?;
let bitmask_len = round_to_next_multiple_of(op_len, 8);

// Returns either an unsigned integer or array of `u8`.
assert!(
dest.layout.ty.is_integral()
|| matches!(dest.layout.ty.kind(), ty::Array(elemty, _) if elemty == &this.tcx.types.u8)
);
assert!(bitmask_len <= 64);
assert_eq!(bitmask_len, dest.layout.size.bits());
let op_len = u32::try_from(op_len).unwrap();

let mut res = 0u64;
for i in 0..op_len {
let op = this.read_immediate(&this.project_index(&op, i.into())?)?;
if simd_element_to_bool(op)? {
res |= 1u64
.checked_shl(simd_bitmask_index(i, op_len, this.data_layout().endian))
.unwrap();
}
}
// We have to change the type of the place to be able to write `res` into it. This
// transmutes the integer to an array, which does the right thing wrt endianess.
let dest =
dest.transmute(this.machine.layouts.uint(dest.layout.size).unwrap(), this)?;
this.write_int(res, &dest)?;
}
"cast" | "as" | "cast_ptr" | "expose_addr" | "from_exposed_addr" => {
let [op] = check_arg_count(args)?;
let (op, op_len) = this.operand_to_simd(op)?;
Expand Down Expand Up @@ -635,34 +656,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
}
}
}
"bitmask" => {
let [op] = check_arg_count(args)?;
let (op, op_len) = this.operand_to_simd(op)?;
let bitmask_len = op_len.max(8);

// Returns either an unsigned integer or array of `u8`.
assert!(
dest.layout.ty.is_integral()
|| matches!(dest.layout.ty.kind(), ty::Array(elemty, _) if elemty == &this.tcx.types.u8)
);
assert!(bitmask_len <= 64);
assert_eq!(bitmask_len, dest.layout.size.bits());
let op_len = u32::try_from(op_len).unwrap();

let mut res = 0u64;
for i in 0..op_len {
let op = this.read_immediate(&this.project_index(&op, i.into())?)?;
if simd_element_to_bool(op)? {
res |= 1u64
.checked_shl(simd_bitmask_index(i, op_len, this.data_layout().endian))
.unwrap();
}
}
// We have to force the place type to be an int so that we can write `res` into it.
let mut dest = this.force_allocation(dest)?;
dest.layout = this.machine.layouts.uint(dest.layout.size).unwrap();
this.write_int(res, &dest)?;
}

name => throw_unsup_format!("unimplemented intrinsic: `simd_{name}`"),
}
Expand Down
6 changes: 3 additions & 3 deletions src/tools/miri/src/shims/unix/mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
//! equivalent. That is the only part we support: no MAP_FIXED or MAP_SHARED or anything
//! else that goes beyond a basic allocation API.

use crate::*;
use crate::{helpers::round_to_next_multiple_of, *};
use rustc_target::abi::Size;

impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
Expand Down Expand Up @@ -89,7 +89,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
}

let align = this.machine.page_align();
let map_length = this.machine.round_up_to_multiple_of_page_size(length).unwrap_or(u64::MAX);
let map_length = round_to_next_multiple_of(length, this.machine.page_size);

let ptr =
this.allocate_ptr(Size::from_bytes(map_length), align, MiriMemoryKind::Mmap.into())?;
Expand Down Expand Up @@ -123,7 +123,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
return Ok(Scalar::from_i32(-1));
}

let length = this.machine.round_up_to_multiple_of_page_size(length).unwrap_or(u64::MAX);
let length = round_to_next_multiple_of(length, this.machine.page_size);

let ptr = Machine::ptr_from_addr_cast(this, addr)?;

Expand Down
12 changes: 2 additions & 10 deletions src/tools/miri/tests/fail/unaligned_pointers/promise_alignment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,7 @@ fn main() {
let _val = unsafe { buffer.read() };

// Let's find a place to promise alignment 8.
let align8 = if buffer.addr() % 8 == 0 {
buffer
} else {
buffer.wrapping_add(1)
};
let align8 = if buffer.addr() % 8 == 0 { buffer } else { buffer.wrapping_add(1) };
assert!(align8.addr() % 8 == 0);
unsafe { utils::miri_promise_symbolic_alignment(align8.cast(), 8) };
// Promising the alignment down to 1 *again* still must not hurt.
Expand All @@ -41,11 +37,7 @@ fn main() {
#[derive(Copy, Clone)]
struct Align16(u128);

let align16 = if align8.addr() % 16 == 0 {
align8
} else {
align8.wrapping_add(2)
};
let align16 = if align8.addr() % 16 == 0 { align8 } else { align8.wrapping_add(2) };
assert!(align16.addr() % 16 == 0);

let _val = unsafe { align8.cast::<Align16>().read() };
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#[path = "../../utils/mod.rs"]
mod utils;

fn main() {
let buffer = [0u32; 128];
unsafe { utils::miri_promise_symbolic_alignment(buffer.as_ptr().cast(), 0) };
//~^ERROR: alignment must be a power of 2
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
error: unsupported operation: `miri_promise_symbolic_alignment`: alignment must be a power of 2, got 0
--> $DIR/promise_alignment_zero.rs:LL:CC
|
LL | unsafe { utils::miri_promise_symbolic_alignment(buffer.as_ptr().cast(), 0) };
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `miri_promise_symbolic_alignment`: alignment must be a power of 2, got 0
|
= help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support
= note: BACKTRACE:
= note: inside `main` at $DIR/promise_alignment_zero.rs:LL:CC

note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace

error: aborting due to 1 previous error

12 changes: 12 additions & 0 deletions src/tools/miri/tests/pass/align_offset_symbolic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,21 @@ fn test_cstr() {
std::ffi::CStr::from_bytes_with_nul(b"this is a test that is longer than 16 bytes\0").unwrap();
}

fn huge_align() {
#[cfg(target_pointer_width = "64")]
const SIZE: usize = 1 << 47;
#[cfg(target_pointer_width = "32")]
const SIZE: usize = 1 << 30;
#[cfg(target_pointer_width = "16")]
const SIZE: usize = 1 << 13;
struct HugeSize([u8; SIZE - 1]);
let _ = std::ptr::invalid::<HugeSize>(SIZE).align_offset(SIZE);
}

fn main() {
test_align_to();
test_from_utf8();
test_u64_array();
test_cstr();
huge_align();
}
22 changes: 12 additions & 10 deletions src/tools/miri/tests/pass/issues/issue-3200-packed-field-offset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,15 @@ impl PackedSized {
}
}

fn main() { unsafe {
let p = PackedSized { f: 0, d: [1, 2, 3, 4] };
let p = p.unsize() as *const PackedUnsized;
// Make sure the size computation does *not* think there is
// any padding in front of the `d` field.
assert_eq!(mem::size_of_val_raw(p), 1 + 4*4);
// And likewise for the offset computation.
let d = std::ptr::addr_of!((*p).d);
assert_eq!(d.cast::<u32>().read_unaligned(), 1);
} }
fn main() {
unsafe {
let p = PackedSized { f: 0, d: [1, 2, 3, 4] };
let p = p.unsize() as *const PackedUnsized;
// Make sure the size computation does *not* think there is
// any padding in front of the `d` field.
assert_eq!(mem::size_of_val_raw(p), 1 + 4 * 4);
// And likewise for the offset computation.
let d = std::ptr::addr_of!((*p).d);
assert_eq!(d.cast::<u32>().read_unaligned(), 1);
}
}
22 changes: 12 additions & 10 deletions src/tools/miri/tests/pass/issues/issue-3200-packed2-field-offset.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,15 @@ impl PackedSized {
}
}

fn main() { unsafe {
let p = PackedSized { f: 0, d: [1, 2, 3, 4] };
let p = p.unsize() as *const PackedUnsized;
// Make sure the size computation correctly adds exact 1 byte of padding
// in front of the `d` field.
assert_eq!(mem::size_of_val_raw(p), 1 + 1 + 4*4);
// And likewise for the offset computation.
let d = std::ptr::addr_of!((*p).d);
assert_eq!(d.cast::<u32>().read_unaligned(), 1);
} }
fn main() {
unsafe {
let p = PackedSized { f: 0, d: [1, 2, 3, 4] };
let p = p.unsize() as *const PackedUnsized;
// Make sure the size computation correctly adds exactly 1 byte of padding
// in front of the `d` field.
assert_eq!(mem::size_of_val_raw(p), 1 + 1 + 4 * 4);
// And likewise for the offset computation.
let d = std::ptr::addr_of!((*p).d);
assert_eq!(d.cast::<u32>().read_unaligned(), 1);
}
}
Loading

0 comments on commit f536185

Please sign in to comment.