From 9164cd164cd8e9033f912bc3a858da9106ed0737 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 10 Dec 2023 22:54:06 +0100 Subject: [PATCH 1/9] ./miri run: default to edition 2021 --- src/tools/miri/miri-script/src/commands.rs | 15 ++++++++++++--- src/tools/miri/tests/compiletest.rs | 2 +- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/tools/miri/miri-script/src/commands.rs b/src/tools/miri/miri-script/src/commands.rs index e4789c696b390..0b3c4fb985bed 100644 --- a/src/tools/miri/miri-script/src/commands.rs +++ b/src/tools/miri/miri-script/src/commands.rs @@ -478,7 +478,11 @@ impl Command { // Scan for "--target" to overwrite the "MIRI_TEST_TARGET" env var so // that we set the MIRI_SYSROOT up the right way. use itertools::Itertools; - let target = flags.iter().tuple_windows().find(|(first, _)| first == &"--target"); + let target = flags + .iter() + .take_while(|arg| *arg != "--") + .tuple_windows() + .find(|(first, _)| *first == "--target"); if let Some((_, target)) = target { // Found it! e.sh.set_var("MIRI_TEST_TARGET", target); @@ -487,6 +491,10 @@ impl Command { let miriflags = e.sh.var("MIRIFLAGS").unwrap_or_default(); e.sh.set_var("MIRIFLAGS", format!("{miriflags} --target {target}")); } + // Scan for "--edition" (we'll set one ourselves if that flag is not present). + let have_edition = + flags.iter().take_while(|arg| *arg != "--").any(|arg| *arg == "--edition"); + // Prepare a sysroot. e.build_miri_sysroot(/* quiet */ true)?; @@ -496,15 +504,16 @@ impl Command { let miri_flags = flagsplit(&miri_flags); let toolchain = &e.toolchain; let extra_flags = &e.cargo_extra_flags; + let edition_flags = (!have_edition).then_some("--edition=2021"); // keep in sync with `compiletest.rs`.` if dep { cmd!( e.sh, - "cargo +{toolchain} --quiet test --test compiletest {extra_flags...} --manifest-path {miri_manifest} -- --miri-run-dep-mode {miri_flags...} {flags...}" + "cargo +{toolchain} --quiet test --test compiletest {extra_flags...} --manifest-path {miri_manifest} -- --miri-run-dep-mode {miri_flags...} {edition_flags...} {flags...}" ).quiet().run()?; } else { cmd!( e.sh, - "cargo +{toolchain} --quiet run {extra_flags...} --manifest-path {miri_manifest} -- {miri_flags...} {flags...}" + "cargo +{toolchain} --quiet run {extra_flags...} --manifest-path {miri_manifest} -- {miri_flags...} {edition_flags...} {flags...}" ).quiet().run()?; } Ok(()) diff --git a/src/tools/miri/tests/compiletest.rs b/src/tools/miri/tests/compiletest.rs index 91df4985c0f0c..3394c4a49f836 100644 --- a/src/tools/miri/tests/compiletest.rs +++ b/src/tools/miri/tests/compiletest.rs @@ -91,7 +91,7 @@ fn test_config(target: &str, path: &str, mode: Mode, with_dependencies: bool) -> mode, program, out_dir: PathBuf::from(std::env::var_os("CARGO_TARGET_DIR").unwrap()).join("ui"), - edition: Some("2021".into()), + edition: Some("2021".into()), // keep in sync with `./miri run` threads: std::env::var("MIRI_TEST_THREADS") .ok() .map(|threads| NonZeroUsize::new(threads.parse().unwrap()).unwrap()), From c54312d07900bfe683c2c9a742cf6413fdb4212e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 17 Dec 2023 10:13:08 +0100 Subject: [PATCH 2/9] rustc-push: automatically fill PR body to avoid reviewer assignment --- src/tools/miri/miri-script/src/commands.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/miri-script/src/commands.rs b/src/tools/miri/miri-script/src/commands.rs index e4789c696b390..83486537fd454 100644 --- a/src/tools/miri/miri-script/src/commands.rs +++ b/src/tools/miri/miri-script/src/commands.rs @@ -344,7 +344,7 @@ impl Command { println!( // Open PR with `subtree update` title to silence the `no-merges` triagebot check // See https://github.com/rust-lang/rust/pull/114157 - " https://github.com/rust-lang/rust/compare/{github_user}:{branch}?quick_pull=1&title=Miri+subtree+update" + " https://github.com/rust-lang/rust/compare/{github_user}:{branch}?quick_pull=1&title=Miri+subtree+update&body=r?+@ghost" ); drop(josh); From 4e2235a4804b3280b754acb2de4516038ddbb3e4 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sun, 10 Dec 2023 12:38:26 -0500 Subject: [PATCH 3/9] Make mmap not use expose semantics --- src/tools/miri/src/shims/unix/linux/mem.rs | 7 +-- src/tools/miri/src/shims/unix/mem.rs | 45 ++++++------------- .../shims/mmap_use_after_munmap.stderr | 17 +------ src/tools/miri/tests/fail-dep/shims/munmap.rs | 22 --------- .../miri/tests/fail-dep/shims/munmap.stderr | 39 ---------------- .../tests/fail-dep/shims/munmap_partial.rs | 8 ++-- .../fail-dep/shims/munmap_partial.stderr | 24 +++------- src/tools/miri/tests/pass-dep/shims/mmap.rs | 5 +-- 8 files changed, 29 insertions(+), 138 deletions(-) delete mode 100644 src/tools/miri/tests/fail-dep/shims/munmap.rs delete mode 100644 src/tools/miri/tests/fail-dep/shims/munmap.stderr diff --git a/src/tools/miri/src/shims/unix/linux/mem.rs b/src/tools/miri/src/shims/unix/linux/mem.rs index 026fd6ae5e7ca..7bbe5618df4e4 100644 --- a/src/tools/miri/src/shims/unix/linux/mem.rs +++ b/src/tools/miri/src/shims/unix/linux/mem.rs @@ -15,14 +15,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let old_address = this.read_target_usize(old_address)?; + let old_address = this.read_pointer(old_address)?; let old_size = this.read_target_usize(old_size)?; let new_size = this.read_target_usize(new_size)?; let flags = this.read_scalar(flags)?.to_i32()?; // old_address must be a multiple of the page size #[allow(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero - if old_address % this.machine.page_size != 0 || new_size == 0 { + if old_address.addr().bytes() % this.machine.page_size != 0 || new_size == 0 { this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?; return Ok(this.eval_libc("MAP_FAILED")); } @@ -41,7 +41,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { return Ok(Scalar::from_maybe_pointer(Pointer::null(), this)); } - let old_address = Machine::ptr_from_addr_cast(this, old_address)?; let align = this.machine.page_align(); let ptr = this.reallocate_ptr( old_address, @@ -59,8 +58,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) .unwrap(); } - // Memory mappings are always exposed - Machine::expose_ptr(this, ptr)?; Ok(Scalar::from_pointer(ptr, this)) } diff --git a/src/tools/miri/src/shims/unix/mem.rs b/src/tools/miri/src/shims/unix/mem.rs index cf36d4b191c7b..88eadb5f46527 100644 --- a/src/tools/miri/src/shims/unix/mem.rs +++ b/src/tools/miri/src/shims/unix/mem.rs @@ -6,6 +6,13 @@ //! mmap/munmap behave a lot like alloc/dealloc, and for simple use they are exactly //! equivalent. That is the only part we support: no MAP_FIXED or MAP_SHARED or anything //! else that goes beyond a basic allocation API. +//! +//! Note that in addition to only supporting malloc-like calls to mmap, we only support free-like +//! calls to munmap, but for a very different reason. In principle, according to the man pages, it +//! is possible to unmap arbitrary regions of address space. But in a high-level language like Rust +//! this amounts to partial deallocation, which LLVM does not support. So any attempt to call our +//! munmap shim which would partily unmap a region of address space previously mapped by mmap will +//! report UB. use crate::{helpers::round_to_next_multiple_of, *}; use rustc_target::abi::Size; @@ -100,8 +107,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { std::iter::repeat(0u8).take(usize::try_from(map_length).unwrap()), ) .unwrap(); - // Memory mappings don't use provenance, and are always exposed. - Machine::expose_ptr(this, ptr)?; Ok(Scalar::from_pointer(ptr, this)) } @@ -113,43 +118,21 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ) -> InterpResult<'tcx, Scalar> { let this = self.eval_context_mut(); - let addr = this.read_target_usize(addr)?; + let addr = this.read_pointer(addr)?; let length = this.read_target_usize(length)?; - // addr must be a multiple of the page size + // addr must be a multiple of the page size, but apart from that munmap is just implemented + // as a dealloc. #[allow(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero - if addr % this.machine.page_size != 0 { + if addr.addr().bytes() % this.machine.page_size != 0 { this.set_last_error(Scalar::from_i32(this.eval_libc_i32("EINVAL")))?; return Ok(Scalar::from_i32(-1)); } - let length = round_to_next_multiple_of(length, this.machine.page_size); - - let ptr = Machine::ptr_from_addr_cast(this, addr)?; - - let Ok(ptr) = ptr.into_pointer_or_addr() else { - throw_unsup_format!("Miri only supports munmap on memory allocated directly by mmap"); - }; - let Some((alloc_id, offset, _prov)) = Machine::ptr_get_alloc(this, ptr) else { - throw_unsup_format!("Miri only supports munmap on memory allocated directly by mmap"); - }; - - // Elsewhere in this function we are careful to check what we can and throw an unsupported - // error instead of Undefined Behavior when use of this function falls outside of the - // narrow scope we support. We deliberately do not check the MemoryKind of this allocation, - // because we want to report UB on attempting to unmap memory that Rust "understands", such - // the stack, heap, or statics. - let (_kind, alloc) = this.memory.alloc_map().get(alloc_id).unwrap(); - if offset != Size::ZERO || alloc.len() as u64 != length { - throw_unsup_format!( - "Miri only supports munmap calls that exactly unmap a region previously returned by mmap" - ); - } - - let len = Size::from_bytes(alloc.len() as u64); + let length = Size::from_bytes(round_to_next_multiple_of(length, this.machine.page_size)); this.deallocate_ptr( - ptr.into(), - Some((len, this.machine.page_align())), + addr, + Some((length, this.machine.page_align())), MemoryKind::Machine(MiriMemoryKind::Mmap), )?; diff --git a/src/tools/miri/tests/fail-dep/shims/mmap_use_after_munmap.stderr b/src/tools/miri/tests/fail-dep/shims/mmap_use_after_munmap.stderr index 21b4baa500922..49f2b84baa85e 100644 --- a/src/tools/miri/tests/fail-dep/shims/mmap_use_after_munmap.stderr +++ b/src/tools/miri/tests/fail-dep/shims/mmap_use_after_munmap.stderr @@ -1,18 +1,3 @@ -warning: integer-to-pointer cast - --> $DIR/mmap_use_after_munmap.rs:LL:CC - | -LL | libc::munmap(ptr, 4096); - | ^^^^^^^^^^^^^^^^^^^^^^^ integer-to-pointer cast - | - = help: This program is using integer-to-pointer casts or (equivalently) `ptr::from_exposed_addr`, - = help: which means that Miri might miss pointer bugs in this program. - = help: See https://doc.rust-lang.org/nightly/std/ptr/fn.from_exposed_addr.html for more details on that operation. - = help: To ensure that Miri does not miss bugs in your program, use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead. - = help: You can then pass the `-Zmiri-strict-provenance` flag to Miri, to ensure you are not relying on `from_exposed_addr` semantics. - = help: Alternatively, the `-Zmiri-permissive-provenance` flag disables this warning. - = note: BACKTRACE: - = note: inside `main` at $DIR/mmap_use_after_munmap.rs:LL:CC - error: Undefined Behavior: memory access failed: ALLOC has been freed, so this pointer is dangling --> $DIR/mmap_use_after_munmap.rs:LL:CC | @@ -43,5 +28,5 @@ LL | libc::munmap(ptr, 4096); note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace -error: aborting due to 1 previous error; 1 warning emitted +error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/fail-dep/shims/munmap.rs b/src/tools/miri/tests/fail-dep/shims/munmap.rs deleted file mode 100644 index 453437a06cfcc..0000000000000 --- a/src/tools/miri/tests/fail-dep/shims/munmap.rs +++ /dev/null @@ -1,22 +0,0 @@ -//@compile-flags: -Zmiri-disable-isolation -//@ignore-target-windows: No libc on Windows - -#![feature(rustc_private)] -#![feature(strict_provenance)] - -use std::ptr; - -fn main() { - // Linux specifies that it is not an error if the specified range does not contain any pages. - // But we simply do not support such calls. This test checks that we report this as - // unsupported, not Undefined Behavior. - let res = unsafe { - libc::munmap( - //~^ ERROR: unsupported operation - // Some high address we surely have not allocated anything at - ptr::invalid_mut(1 << 30), - 4096, - ) - }; - assert_eq!(res, 0); -} diff --git a/src/tools/miri/tests/fail-dep/shims/munmap.stderr b/src/tools/miri/tests/fail-dep/shims/munmap.stderr deleted file mode 100644 index f17473677f636..0000000000000 --- a/src/tools/miri/tests/fail-dep/shims/munmap.stderr +++ /dev/null @@ -1,39 +0,0 @@ -warning: integer-to-pointer cast - --> $DIR/munmap.rs:LL:CC - | -LL | / libc::munmap( -LL | | -LL | | // Some high address we surely have not allocated anything at -LL | | ptr::invalid_mut(1 << 30), -LL | | 4096, -LL | | ) - | |_________^ integer-to-pointer cast - | - = help: This program is using integer-to-pointer casts or (equivalently) `ptr::from_exposed_addr`, - = help: which means that Miri might miss pointer bugs in this program. - = help: See https://doc.rust-lang.org/nightly/std/ptr/fn.from_exposed_addr.html for more details on that operation. - = help: To ensure that Miri does not miss bugs in your program, use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead. - = help: You can then pass the `-Zmiri-strict-provenance` flag to Miri, to ensure you are not relying on `from_exposed_addr` semantics. - = help: Alternatively, the `-Zmiri-permissive-provenance` flag disables this warning. - = note: BACKTRACE: - = note: inside `main` at $DIR/munmap.rs:LL:CC - -error: unsupported operation: Miri only supports munmap on memory allocated directly by mmap - --> $DIR/munmap.rs:LL:CC - | -LL | / libc::munmap( -LL | | -LL | | // Some high address we surely have not allocated anything at -LL | | ptr::invalid_mut(1 << 30), -LL | | 4096, -LL | | ) - | |_________^ Miri only supports munmap on memory allocated directly by mmap - | - = 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/munmap.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; 1 warning emitted - diff --git a/src/tools/miri/tests/fail-dep/shims/munmap_partial.rs b/src/tools/miri/tests/fail-dep/shims/munmap_partial.rs index 938850ee2862b..1d6e4796d0a57 100644 --- a/src/tools/miri/tests/fail-dep/shims/munmap_partial.rs +++ b/src/tools/miri/tests/fail-dep/shims/munmap_partial.rs @@ -1,6 +1,8 @@ -//! Our mmap/munmap support is a thin wrapper over Interpcx::allocate_ptr. Since the underlying -//! layer has much more UB than munmap does, we need to be sure we throw an unsupported error here. +//! The man pages for mmap/munmap suggest that it is possible to partly unmap a previously-mapped +//! region of addres space, but to LLVM that would be partial deallocation, which LLVM does not +//! support. So even though the man pages say this sort of use is possible, we must report UB. //@ignore-target-windows: No libc on Windows +//@normalize-stderr-test: "size [0-9]+ and alignment" -> "size SIZE and alignment" fn main() { unsafe { @@ -13,6 +15,6 @@ fn main() { 0, ); libc::munmap(ptr, 1); - //~^ ERROR: unsupported operation + //~^ ERROR: Undefined Behavior } } diff --git a/src/tools/miri/tests/fail-dep/shims/munmap_partial.stderr b/src/tools/miri/tests/fail-dep/shims/munmap_partial.stderr index 14eb9d32053ce..39825eb27c083 100644 --- a/src/tools/miri/tests/fail-dep/shims/munmap_partial.stderr +++ b/src/tools/miri/tests/fail-dep/shims/munmap_partial.stderr @@ -1,29 +1,15 @@ -warning: integer-to-pointer cast +error: Undefined Behavior: incorrect layout on deallocation: ALLOC has size SIZE and alignment ALIGN, but gave size SIZE and alignment ALIGN --> $DIR/munmap_partial.rs:LL:CC | LL | libc::munmap(ptr, 1); - | ^^^^^^^^^^^^^^^^^^^^ integer-to-pointer cast + | ^^^^^^^^^^^^^^^^^^^^ incorrect layout on deallocation: ALLOC has size SIZE and alignment ALIGN, but gave size SIZE and alignment ALIGN | - = help: This program is using integer-to-pointer casts or (equivalently) `ptr::from_exposed_addr`, - = help: which means that Miri might miss pointer bugs in this program. - = help: See https://doc.rust-lang.org/nightly/std/ptr/fn.from_exposed_addr.html for more details on that operation. - = help: To ensure that Miri does not miss bugs in your program, use Strict Provenance APIs (https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance, https://crates.io/crates/sptr) instead. - = help: You can then pass the `-Zmiri-strict-provenance` flag to Miri, to ensure you are not relying on `from_exposed_addr` semantics. - = help: Alternatively, the `-Zmiri-permissive-provenance` flag disables this warning. - = note: BACKTRACE: - = note: inside `main` at $DIR/munmap_partial.rs:LL:CC - -error: unsupported operation: Miri only supports munmap calls that exactly unmap a region previously returned by mmap - --> $DIR/munmap_partial.rs:LL:CC - | -LL | libc::munmap(ptr, 1); - | ^^^^^^^^^^^^^^^^^^^^ Miri only supports munmap calls that exactly unmap a region previously returned by mmap - | - = help: this is likely not a bug in the program; it indicates that the program performed an operation that the interpreter does not support + = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior + = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: BACKTRACE: = note: inside `main` at $DIR/munmap_partial.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; 1 warning emitted +error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/pass-dep/shims/mmap.rs b/src/tools/miri/tests/pass-dep/shims/mmap.rs index 03ffc3b38986f..72802a7f8ca92 100644 --- a/src/tools/miri/tests/pass-dep/shims/mmap.rs +++ b/src/tools/miri/tests/pass-dep/shims/mmap.rs @@ -28,9 +28,8 @@ fn test_mmap() { } assert!(slice.iter().all(|b| *b == 1)); - // Ensure that we can munmap with just an integer - let just_an_address = ptr::invalid_mut(ptr.addr()); - let res = unsafe { libc::munmap(just_an_address, page_size) }; + // Ensure that we can munmap + let res = unsafe { libc::munmap(ptr, page_size) }; assert_eq!(res, 0i32); } From 245f2ad429011498b13d090438a605fba0655c58 Mon Sep 17 00:00:00 2001 From: The Miri Conjob Bot Date: Tue, 19 Dec 2023 04:54:11 +0000 Subject: [PATCH 4/9] Preparing for merge from rustc --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index e82b7f841543a..e008de74284b3 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -604f185fae9a4b0edf7e28f616a0f53880f8f074 +e999d8b6e137c0393470a2d846047ee86c177719 From f0b84564d4ebb74995a1f343a0976439ce08fc10 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 21 Dec 2023 17:44:27 +0100 Subject: [PATCH 5/9] Preparing for merge from rustc --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index e008de74284b3..f67ac80c52697 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -e999d8b6e137c0393470a2d846047ee86c177719 +767453eb7ca188e991ac5568c17b984dd4893e77 From 84304fc00a2b582b1797a52dfaa954ff36b4ff91 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 22 Dec 2023 12:03:54 +0100 Subject: [PATCH 6/9] simd_scatter/gather: test OOB cases and the order of writes --- src/tools/miri/tests/pass/portable-simd.rs | 45 +++++++++++++++++++--- 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/src/tools/miri/tests/pass/portable-simd.rs b/src/tools/miri/tests/pass/portable-simd.rs index f370e658272f5..3d24943293c92 100644 --- a/src/tools/miri/tests/pass/portable-simd.rs +++ b/src/tools/miri/tests/pass/portable-simd.rs @@ -1,6 +1,8 @@ //@compile-flags: -Zmiri-strict-provenance -#![feature(portable_simd, platform_intrinsics, adt_const_params, inline_const)] +#![feature(portable_simd, platform_intrinsics, adt_const_params, inline_const, core_intrinsics)] #![allow(incomplete_features, internal_features)] +use std::intrinsics::simd as intrinsics; +use std::ptr; use std::simd::{prelude::*, StdFloat}; fn simd_ops_f32() { @@ -421,6 +423,40 @@ fn simd_gather_scatter() { let idxs = Simd::from_array([9, 3, 0, 0]); Simd::from_array([-27, 82, -41, 124]).scatter(&mut vec, idxs); assert_eq!(vec, vec![124, 11, 12, 82, 14, 15, 16, 17, 18]); + + // We call the intrinsics directly to experiment with dangling pointers and masks. + let val = 42u8; + let ptrs: Simd<*const u8, 4> = + Simd::from_array([ptr::null(), ptr::addr_of!(val), ptr::addr_of!(val), ptr::addr_of!(val)]); + let default = u8x4::splat(0); + let mask = i8x4::from_array([0, !0, 0, !0]); + let vals = unsafe { intrinsics::simd_gather(default, ptrs, mask) }; + assert_eq!(vals, u8x4::from_array([0, 42, 0, 42]),); + + let mut val1 = 0u8; + let mut val2 = 0u8; + let ptrs: Simd<*mut u8, 4> = Simd::from_array([ + ptr::null_mut(), + ptr::addr_of_mut!(val1), + ptr::addr_of_mut!(val1), + ptr::addr_of_mut!(val2), + ]); + let vals = u8x4::from_array([1, 2, 3, 4]); + unsafe { intrinsics::simd_scatter(vals, ptrs, mask) }; + assert_eq!(val1, 2); + assert_eq!(val2, 4); + + // Also check what happens when `scatter` has multiple overlapping pointers. + let mut val = 0u8; + let ptrs: Simd<*mut u8, 4> = Simd::from_array([ + ptr::addr_of_mut!(val), + ptr::addr_of_mut!(val), + ptr::addr_of_mut!(val), + ptr::addr_of_mut!(val), + ]); + let vals = u8x4::from_array([1, 2, 3, 4]); + unsafe { intrinsics::simd_scatter(vals, ptrs, mask) }; + assert_eq!(val, 4); } fn simd_round() { @@ -460,14 +496,11 @@ fn simd_round() { } fn simd_intrinsics() { + use intrinsics::*; extern "platform-intrinsic" { - fn simd_eq(x: T, y: T) -> U; - fn simd_reduce_any(x: T) -> bool; - fn simd_reduce_all(x: T) -> bool; - fn simd_select(m: M, yes: T, no: T) -> T; fn simd_shuffle_generic(x: T, y: T) -> U; - fn simd_shuffle(x: T, y: T, idx: IDX) -> U; } + unsafe { // Make sure simd_eq returns all-1 for `true` let a = i32x4::splat(10); From e8a4bd17f3e3821e31db58b50facdc8ed134852a Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 22 Dec 2023 12:25:46 +0100 Subject: [PATCH 7/9] implement and test simd_masked_load and simd_masked_store --- src/tools/miri/src/shims/intrinsics/simd.rs | 48 +++++++++++++++++++++ src/tools/miri/tests/pass/portable-simd.rs | 24 +++++++++++ 2 files changed, 72 insertions(+) diff --git a/src/tools/miri/src/shims/intrinsics/simd.rs b/src/tools/miri/src/shims/intrinsics/simd.rs index e17c06be9b837..2c8493d8aad1a 100644 --- a/src/tools/miri/src/shims/intrinsics/simd.rs +++ b/src/tools/miri/src/shims/intrinsics/simd.rs @@ -656,6 +656,54 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } } } + "masked_load" => { + let [mask, ptr, default] = check_arg_count(args)?; + let (mask, mask_len) = this.operand_to_simd(mask)?; + let ptr = this.read_pointer(ptr)?; + let (default, default_len) = this.operand_to_simd(default)?; + let (dest, dest_len) = this.place_to_simd(dest)?; + + assert_eq!(dest_len, mask_len); + assert_eq!(dest_len, default_len); + + for i in 0..dest_len { + let mask = this.read_immediate(&this.project_index(&mask, i)?)?; + let default = this.read_immediate(&this.project_index(&default, i)?)?; + let dest = this.project_index(&dest, i)?; + + let val = if simd_element_to_bool(mask)? { + // Size * u64 is implemented as always checked + #[allow(clippy::arithmetic_side_effects)] + let ptr = ptr.wrapping_offset(dest.layout.size * i, this); + let place = this.ptr_to_mplace(ptr, dest.layout); + this.read_immediate(&place)? + } else { + default + }; + this.write_immediate(*val, &dest)?; + } + } + "masked_store" => { + let [mask, ptr, vals] = check_arg_count(args)?; + let (mask, mask_len) = this.operand_to_simd(mask)?; + let ptr = this.read_pointer(ptr)?; + let (vals, vals_len) = this.operand_to_simd(vals)?; + + assert_eq!(mask_len, vals_len); + + for i in 0..vals_len { + let mask = this.read_immediate(&this.project_index(&mask, i)?)?; + let val = this.read_immediate(&this.project_index(&vals, i)?)?; + + if simd_element_to_bool(mask)? { + // Size * u64 is implemented as always checked + #[allow(clippy::arithmetic_side_effects)] + let ptr = ptr.wrapping_offset(val.layout.size * i, this); + let place = this.ptr_to_mplace(ptr, val.layout); + this.write_immediate(*val, &place)? + }; + } + } name => throw_unsup_format!("unimplemented intrinsic: `simd_{name}`"), } diff --git a/src/tools/miri/tests/pass/portable-simd.rs b/src/tools/miri/tests/pass/portable-simd.rs index 3d24943293c92..57d0b6a87b22a 100644 --- a/src/tools/miri/tests/pass/portable-simd.rs +++ b/src/tools/miri/tests/pass/portable-simd.rs @@ -536,6 +536,29 @@ fn simd_intrinsics() { } } +fn simd_masked_loadstore() { + // The buffer is deliberarely too short, so reading the last element would be UB. + let buf = [3i32; 3]; + let default = i32x4::splat(0); + let mask = i32x4::from_array([!0, !0, !0, 0]); + let vals = unsafe { intrinsics::simd_masked_load(mask, buf.as_ptr(), default) }; + assert_eq!(vals, i32x4::from_array([3, 3, 3, 0])); + // Also read in a way that the *first* element is OOB. + let mask2 = i32x4::from_array([0, !0, !0, !0]); + let vals = + unsafe { intrinsics::simd_masked_load(mask2, buf.as_ptr().wrapping_sub(1), default) }; + assert_eq!(vals, i32x4::from_array([0, 3, 3, 3])); + + // The buffer is deliberarely too short, so writing the last element would be UB. + let mut buf = [42i32; 3]; + let vals = i32x4::from_array([1, 2, 3, 4]); + unsafe { intrinsics::simd_masked_store(mask, buf.as_mut_ptr(), vals) }; + assert_eq!(buf, [1, 2, 3]); + // Also write in a way that the *first* element is OOB. + unsafe { intrinsics::simd_masked_store(mask2, buf.as_mut_ptr().wrapping_sub(1), vals) }; + assert_eq!(buf, [2, 3, 4]); +} + fn main() { simd_mask(); simd_ops_f32(); @@ -546,4 +569,5 @@ fn main() { simd_gather_scatter(); simd_round(); simd_intrinsics(); + simd_masked_loadstore(); } From f3db65df94bf9a086837f979b06de2c966125c07 Mon Sep 17 00:00:00 2001 From: The Miri Conjob Bot Date: Sun, 24 Dec 2023 04:54:07 +0000 Subject: [PATCH 8/9] Preparing for merge from rustc --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index f67ac80c52697..6af13147997df 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -767453eb7ca188e991ac5568c17b984dd4893e77 +3166bbef9248fce2695899e21203f42a21046551 From f2407d98de61888e287efc97dbc4104f3581b70d Mon Sep 17 00:00:00 2001 From: The Miri Conjob Bot Date: Tue, 26 Dec 2023 04:54:17 +0000 Subject: [PATCH 9/9] Preparing for merge from rustc --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 6af13147997df..5298ff36f2141 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -3166bbef9248fce2695899e21203f42a21046551 +2271c26e4a8e062bb00d709d0ccb5846e0c341b9