mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-31 09:04:18 +00:00
Auto merge of #119315 - RalfJung:miri, r=RalfJung
Miri subtree update r? `@ghost`
This commit is contained in:
commit
ea7ef7b6c2
@ -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);
|
||||
@ -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(())
|
||||
|
@ -1 +1 @@
|
||||
604f185fae9a4b0edf7e28f616a0f53880f8f074
|
||||
2271c26e4a8e062bb00d709d0ccb5846e0c341b9
|
||||
|
@ -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}`"),
|
||||
}
|
||||
|
@ -15,14 +15,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
||||
) -> InterpResult<'tcx, Scalar<Provenance>> {
|
||||
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(this.eval_libc("MAP_FAILED"));
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
@ -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<Provenance>> {
|
||||
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),
|
||||
)?;
|
||||
|
||||
|
@ -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()),
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -29,9 +29,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);
|
||||
|
||||
// Test all of our error conditions
|
||||
|
@ -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<T, U>(x: T, y: T) -> U;
|
||||
fn simd_reduce_any<T>(x: T) -> bool;
|
||||
fn simd_reduce_all<T>(x: T) -> bool;
|
||||
fn simd_select<M, T>(m: M, yes: T, no: T) -> T;
|
||||
fn simd_shuffle_generic<T, U, const IDX: &'static [u32]>(x: T, y: T) -> U;
|
||||
fn simd_shuffle<T, IDX, U>(x: T, y: T, idx: IDX) -> U;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
// Make sure simd_eq returns all-1 for `true`
|
||||
let a = i32x4::splat(10);
|
||||
@ -503,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();
|
||||
@ -513,4 +569,5 @@ fn main() {
|
||||
simd_gather_scatter();
|
||||
simd_round();
|
||||
simd_intrinsics();
|
||||
simd_masked_loadstore();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user