Skip to content

Commit f536185

Browse files
committed
Auto merge of rust-lang#118640 - RalfJung:miri, r=RalfJung
Miri subtree update needed to fix some errors in miri-test-libstd
2 parents f67523d + 73db2e4 commit f536185

13 files changed

+185
-107
lines changed

src/tools/miri/rust-version

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
225e36cff9809948d6567ab16f75d7b087ea83a7
1+
317d14a56cb8c748bf0e2f2afff89c2249ab4423

src/tools/miri/src/helpers.rs

+8
Original file line numberDiff line numberDiff line change
@@ -1188,3 +1188,11 @@ pub(crate) fn simd_element_to_bool(elem: ImmTy<'_, Provenance>) -> InterpResult<
11881188
_ => throw_ub_format!("each element of a SIMD mask must be all-0-bits or all-1-bits"),
11891189
})
11901190
}
1191+
1192+
// This looks like something that would be nice to have in the standard library...
1193+
pub(crate) fn round_to_next_multiple_of(x: u64, divisor: u64) -> u64 {
1194+
assert_ne!(divisor, 0);
1195+
// divisor is nonzero; multiplication cannot overflow since we just divided
1196+
#[allow(clippy::arithmetic_side_effects)]
1197+
return (x.checked_add(divisor - 1).unwrap() / divisor) * divisor;
1198+
}

src/tools/miri/src/machine.rs

-5
Original file line numberDiff line numberDiff line change
@@ -777,11 +777,6 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
777777
drop(self.profiler.take());
778778
}
779779

780-
pub(crate) fn round_up_to_multiple_of_page_size(&self, length: u64) -> Option<u64> {
781-
#[allow(clippy::arithmetic_side_effects)] // page size is nonzero
782-
(length.checked_add(self.page_size - 1)? / self.page_size).checked_mul(self.page_size)
783-
}
784-
785780
pub(crate) fn page_align(&self) -> Align {
786781
Align::from_bytes(self.page_size).unwrap()
787782
}

src/tools/miri/src/shims/foreign_items.rs

+14-4
Original file line numberDiff line numberDiff line change
@@ -558,16 +558,26 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
558558

559559
// Promises that a pointer has a given symbolic alignment.
560560
"miri_promise_symbolic_alignment" => {
561+
use rustc_target::abi::AlignFromBytesError;
562+
561563
let [ptr, align] = this.check_shim(abi, Abi::Rust, link_name, args)?;
562564
let ptr = this.read_pointer(ptr)?;
563565
let align = this.read_target_usize(align)?;
564-
let Ok(align) = Align::from_bytes(align) else {
566+
if !align.is_power_of_two() {
565567
throw_unsup_format!(
566-
"`miri_promise_symbolic_alignment`: alignment must be a power of 2"
568+
"`miri_promise_symbolic_alignment`: alignment must be a power of 2, got {align}"
567569
);
568-
};
570+
}
571+
let align = Align::from_bytes(align).unwrap_or_else(|err| {
572+
match err {
573+
AlignFromBytesError::NotPowerOfTwo(_) => unreachable!(),
574+
// When the alignment is a power of 2 but too big, clamp it to MAX.
575+
AlignFromBytesError::TooLarge(_) => Align::MAX,
576+
}
577+
});
569578
let (_, addr) = ptr.into_parts(); // we know the offset is absolute
570-
if addr.bytes() % align.bytes() != 0 {
579+
// Cannot panic since `align` is a power of 2 and hence non-zero.
580+
if addr.bytes().checked_rem(align.bytes()).unwrap() != 0 {
571581
throw_unsup_format!(
572582
"`miri_promise_symbolic_alignment`: pointer is not actually aligned"
573583
);

src/tools/miri/src/shims/intrinsics/simd.rs

+50-57
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ use rustc_middle::{mir, ty, ty::FloatTy};
44
use rustc_span::{sym, Symbol};
55
use rustc_target::abi::{Endian, HasDataLayout};
66

7+
use crate::helpers::{
8+
bool_to_simd_element, check_arg_count, round_to_next_multiple_of, simd_element_to_bool,
9+
};
710
use crate::*;
8-
use helpers::{bool_to_simd_element, check_arg_count, simd_element_to_bool};
911

1012
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
1113
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
@@ -113,18 +115,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
113115
}
114116
}
115117
Op::Numeric(name) => {
116-
assert!(op.layout.ty.is_integral());
117-
let size = op.layout.size;
118-
let bits = op.to_scalar().to_bits(size).unwrap();
119-
let extra = 128u128.checked_sub(u128::from(size.bits())).unwrap();
120-
let bits_out = match name {
121-
sym::ctlz => u128::from(bits.leading_zeros()).checked_sub(extra).unwrap(),
122-
sym::cttz => u128::from((bits << extra).trailing_zeros()).checked_sub(extra).unwrap(),
123-
sym::bswap => (bits << extra).swap_bytes(),
124-
sym::bitreverse => (bits << extra).reverse_bits(),
125-
_ => unreachable!(),
126-
};
127-
Scalar::from_uint(bits_out, size)
118+
this.numeric_intrinsic(name, op.to_scalar(), op.layout)?
128119
}
129120
};
130121
this.write_scalar(val, &dest)?;
@@ -405,37 +396,35 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
405396
this.write_immediate(*val, &dest)?;
406397
}
407398
}
399+
// Variant of `select` that takes a bitmask rather than a "vector of bool".
408400
"select_bitmask" => {
409401
let [mask, yes, no] = check_arg_count(args)?;
410402
let (yes, yes_len) = this.operand_to_simd(yes)?;
411403
let (no, no_len) = this.operand_to_simd(no)?;
412404
let (dest, dest_len) = this.place_to_simd(dest)?;
413-
let bitmask_len = dest_len.max(8);
405+
let bitmask_len = round_to_next_multiple_of(dest_len, 8);
414406

407+
// The mask must be an integer or an array.
408+
assert!(
409+
mask.layout.ty.is_integral()
410+
|| matches!(mask.layout.ty.kind(), ty::Array(elemty, _) if elemty == &this.tcx.types.u8)
411+
);
415412
assert!(bitmask_len <= 64);
416413
assert_eq!(bitmask_len, mask.layout.size.bits());
417414
assert_eq!(dest_len, yes_len);
418415
assert_eq!(dest_len, no_len);
419416
let dest_len = u32::try_from(dest_len).unwrap();
420417
let bitmask_len = u32::try_from(bitmask_len).unwrap();
421418

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

434425
for i in 0..dest_len {
435-
let mask = mask
436-
& 1u64
437-
.checked_shl(simd_bitmask_index(i, dest_len, this.data_layout().endian))
438-
.unwrap();
426+
let bit_i = simd_bitmask_index(i, dest_len, this.data_layout().endian);
427+
let mask = mask & 1u64.checked_shl(bit_i).unwrap();
439428
let yes = this.read_immediate(&this.project_index(&yes, i.into())?)?;
440429
let no = this.read_immediate(&this.project_index(&no, i.into())?)?;
441430
let dest = this.project_index(&dest, i.into())?;
@@ -445,6 +434,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
445434
}
446435
for i in dest_len..bitmask_len {
447436
// If the mask is "padded", ensure that padding is all-zero.
437+
// This deliberately does not use `simd_bitmask_index`; these bits are outside
438+
// the bitmask. It does not matter in which order we check them.
448439
let mask = mask & 1u64.checked_shl(i).unwrap();
449440
if mask != 0 {
450441
throw_ub_format!(
@@ -453,6 +444,36 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
453444
}
454445
}
455446
}
447+
// Converts a "vector of bool" into a bitmask.
448+
"bitmask" => {
449+
let [op] = check_arg_count(args)?;
450+
let (op, op_len) = this.operand_to_simd(op)?;
451+
let bitmask_len = round_to_next_multiple_of(op_len, 8);
452+
453+
// Returns either an unsigned integer or array of `u8`.
454+
assert!(
455+
dest.layout.ty.is_integral()
456+
|| matches!(dest.layout.ty.kind(), ty::Array(elemty, _) if elemty == &this.tcx.types.u8)
457+
);
458+
assert!(bitmask_len <= 64);
459+
assert_eq!(bitmask_len, dest.layout.size.bits());
460+
let op_len = u32::try_from(op_len).unwrap();
461+
462+
let mut res = 0u64;
463+
for i in 0..op_len {
464+
let op = this.read_immediate(&this.project_index(&op, i.into())?)?;
465+
if simd_element_to_bool(op)? {
466+
res |= 1u64
467+
.checked_shl(simd_bitmask_index(i, op_len, this.data_layout().endian))
468+
.unwrap();
469+
}
470+
}
471+
// We have to change the type of the place to be able to write `res` into it. This
472+
// transmutes the integer to an array, which does the right thing wrt endianess.
473+
let dest =
474+
dest.transmute(this.machine.layouts.uint(dest.layout.size).unwrap(), this)?;
475+
this.write_int(res, &dest)?;
476+
}
456477
"cast" | "as" | "cast_ptr" | "expose_addr" | "from_exposed_addr" => {
457478
let [op] = check_arg_count(args)?;
458479
let (op, op_len) = this.operand_to_simd(op)?;
@@ -635,34 +656,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
635656
}
636657
}
637658
}
638-
"bitmask" => {
639-
let [op] = check_arg_count(args)?;
640-
let (op, op_len) = this.operand_to_simd(op)?;
641-
let bitmask_len = op_len.max(8);
642-
643-
// Returns either an unsigned integer or array of `u8`.
644-
assert!(
645-
dest.layout.ty.is_integral()
646-
|| matches!(dest.layout.ty.kind(), ty::Array(elemty, _) if elemty == &this.tcx.types.u8)
647-
);
648-
assert!(bitmask_len <= 64);
649-
assert_eq!(bitmask_len, dest.layout.size.bits());
650-
let op_len = u32::try_from(op_len).unwrap();
651-
652-
let mut res = 0u64;
653-
for i in 0..op_len {
654-
let op = this.read_immediate(&this.project_index(&op, i.into())?)?;
655-
if simd_element_to_bool(op)? {
656-
res |= 1u64
657-
.checked_shl(simd_bitmask_index(i, op_len, this.data_layout().endian))
658-
.unwrap();
659-
}
660-
}
661-
// We have to force the place type to be an int so that we can write `res` into it.
662-
let mut dest = this.force_allocation(dest)?;
663-
dest.layout = this.machine.layouts.uint(dest.layout.size).unwrap();
664-
this.write_int(res, &dest)?;
665-
}
666659

667660
name => throw_unsup_format!("unimplemented intrinsic: `simd_{name}`"),
668661
}

src/tools/miri/src/shims/unix/mem.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
//! equivalent. That is the only part we support: no MAP_FIXED or MAP_SHARED or anything
88
//! else that goes beyond a basic allocation API.
99
10-
use crate::*;
10+
use crate::{helpers::round_to_next_multiple_of, *};
1111
use rustc_target::abi::Size;
1212

1313
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
@@ -89,7 +89,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
8989
}
9090

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

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

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

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

src/tools/miri/tests/fail/unaligned_pointers/promise_alignment.rs

+2-10
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,7 @@ fn main() {
1717
let _val = unsafe { buffer.read() };
1818

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

44-
let align16 = if align8.addr() % 16 == 0 {
45-
align8
46-
} else {
47-
align8.wrapping_add(2)
48-
};
40+
let align16 = if align8.addr() % 16 == 0 { align8 } else { align8.wrapping_add(2) };
4941
assert!(align16.addr() % 16 == 0);
5042

5143
let _val = unsafe { align8.cast::<Align16>().read() };
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#[path = "../../utils/mod.rs"]
2+
mod utils;
3+
4+
fn main() {
5+
let buffer = [0u32; 128];
6+
unsafe { utils::miri_promise_symbolic_alignment(buffer.as_ptr().cast(), 0) };
7+
//~^ERROR: alignment must be a power of 2
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: unsupported operation: `miri_promise_symbolic_alignment`: alignment must be a power of 2, got 0
2+
--> $DIR/promise_alignment_zero.rs:LL:CC
3+
|
4+
LL | unsafe { utils::miri_promise_symbolic_alignment(buffer.as_ptr().cast(), 0) };
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `miri_promise_symbolic_alignment`: alignment must be a power of 2, got 0
6+
|
7+
= help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support
8+
= note: BACKTRACE:
9+
= note: inside `main` at $DIR/promise_alignment_zero.rs:LL:CC
10+
11+
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
12+
13+
error: aborting due to 1 previous error
14+

src/tools/miri/tests/pass/align_offset_symbolic.rs

+12
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,21 @@ fn test_cstr() {
9090
std::ffi::CStr::from_bytes_with_nul(b"this is a test that is longer than 16 bytes\0").unwrap();
9191
}
9292

93+
fn huge_align() {
94+
#[cfg(target_pointer_width = "64")]
95+
const SIZE: usize = 1 << 47;
96+
#[cfg(target_pointer_width = "32")]
97+
const SIZE: usize = 1 << 30;
98+
#[cfg(target_pointer_width = "16")]
99+
const SIZE: usize = 1 << 13;
100+
struct HugeSize([u8; SIZE - 1]);
101+
let _ = std::ptr::invalid::<HugeSize>(SIZE).align_offset(SIZE);
102+
}
103+
93104
fn main() {
94105
test_align_to();
95106
test_from_utf8();
96107
test_u64_array();
97108
test_cstr();
109+
huge_align();
98110
}

src/tools/miri/tests/pass/issues/issue-3200-packed-field-offset.rs

+12-10
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,15 @@ impl PackedSized {
2323
}
2424
}
2525

26-
fn main() { unsafe {
27-
let p = PackedSized { f: 0, d: [1, 2, 3, 4] };
28-
let p = p.unsize() as *const PackedUnsized;
29-
// Make sure the size computation does *not* think there is
30-
// any padding in front of the `d` field.
31-
assert_eq!(mem::size_of_val_raw(p), 1 + 4*4);
32-
// And likewise for the offset computation.
33-
let d = std::ptr::addr_of!((*p).d);
34-
assert_eq!(d.cast::<u32>().read_unaligned(), 1);
35-
} }
26+
fn main() {
27+
unsafe {
28+
let p = PackedSized { f: 0, d: [1, 2, 3, 4] };
29+
let p = p.unsize() as *const PackedUnsized;
30+
// Make sure the size computation does *not* think there is
31+
// any padding in front of the `d` field.
32+
assert_eq!(mem::size_of_val_raw(p), 1 + 4 * 4);
33+
// And likewise for the offset computation.
34+
let d = std::ptr::addr_of!((*p).d);
35+
assert_eq!(d.cast::<u32>().read_unaligned(), 1);
36+
}
37+
}

src/tools/miri/tests/pass/issues/issue-3200-packed2-field-offset.rs

+12-10
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,15 @@ impl PackedSized {
2626
}
2727
}
2828

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

0 commit comments

Comments
 (0)