Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Miri subtree update #118640

Merged
merged 20 commits into from
Dec 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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