diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md index 86f190134d452..2832ef50adef9 100644 --- a/src/tools/miri/README.md +++ b/src/tools/miri/README.md @@ -219,7 +219,7 @@ degree documented below): - We have unofficial support (not maintained by the Miri team itself) for some further operating systems. - `solaris` / `illumos`: maintained by @devnexen. Supports the entire test suite. - `freebsd`: maintained by @YohDeadfall and @LorrensP-2158466. Supports the entire test suite. - - `android`: **maintainer wanted**. Support very incomplete, but a basic "hello world" works. + - `android`: **maintainer wanted**. Basic OS APIs and concurrency work, but file system access is not supported. - For targets on other operating systems, Miri might fail before even reaching the `main` function. However, even for targets that we do support, the degree of support for accessing platform APIs diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index e79ad9362c085..024cbd2852073 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -dc47a69ed94bc88b10b7d500cceacf29b87bcbbe +cb79c42008b970269f6a06b257e5f04b93f24d03 diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs index bd4573f94013e..064c1cc5b95f7 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs @@ -374,9 +374,9 @@ impl Permission { self.inner.strongest_idempotent_foreign_access(prot) } - /// Returns the strongest access allowed from a child to this node without + /// Returns the strongest access allowed that is local to this node without /// causing UB (only considers possible transitions to this permission). - pub fn strongest_allowed_child_access(&self, protected: bool) -> WildcardAccessLevel { + pub fn strongest_allowed_local_access(&self, protected: bool) -> WildcardAccessLevel { match self.inner { // Everything except disabled can be accessed by read access. Disabled => WildcardAccessLevel::None, @@ -794,9 +794,9 @@ mod propagation_optimization_checks { /// Checks that `strongest_allowed_child_access` correctly /// represents which transitions are possible. #[test] - fn strongest_allowed_child_access() { + fn strongest_allowed_local_access() { for (permission, protected) in <(Permission, bool)>::exhaustive() { - let strongest_child_access = permission.strongest_allowed_child_access(protected); + let strongest_local_access = permission.strongest_allowed_local_access(protected); let is_read_valid = Permission::perform_access( AccessKind::Read, @@ -814,8 +814,8 @@ mod propagation_optimization_checks { ) .is_some(); - assert_eq!(is_read_valid, strongest_child_access >= WildcardAccessLevel::Read); - assert_eq!(is_write_valid, strongest_child_access >= WildcardAccessLevel::Write); + assert_eq!(is_read_valid, strongest_local_access >= WildcardAccessLevel::Read); + assert_eq!(is_write_valid, strongest_local_access >= WildcardAccessLevel::Write); } } } diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs index 900e9c3729c84..c8e27ebf0ff32 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs @@ -111,7 +111,7 @@ impl LocationState { // We need to update the wildcard state, if the permission // of an exposed pointer changes. if node.is_exposed { - let access_type = self.permission.strongest_allowed_child_access(protected); + let access_type = self.permission.strongest_allowed_local_access(protected); WildcardState::update_exposure(idx, access_type, nodes, wildcard_accesses); } } @@ -1034,6 +1034,9 @@ impl<'tcx> LocationTree { wildcard_state.access_relatedness(access_kind, only_foreign) }; + // Whether there is an exposed node in this tree that allows this access. + let mut has_valid_exposed = false; + // This does a traversal across the tree updating children before their parents. The // difference to `perform_normal_access` is that we take the access relatedness from // the wildcard tracking state of the node instead of from the visitor itself. @@ -1082,6 +1085,17 @@ impl<'tcx> LocationTree { return Err(no_valid_exposed_references_error(diagnostics)); }; + let mut entry = args.data.perms.entry(args.idx); + let perm = entry.or_insert(node.default_location_state()); + + // We only count exposed nodes through which an access could happen. + if node.is_exposed + && perm.permission.strongest_allowed_local_access(protected).allows(access_kind) + && max_local_tag.is_none_or(|max_local_tag| max_local_tag >= node.tag) + { + has_valid_exposed = true; + } + let Some(relatedness) = wildcard_relatedness.to_relatedness() else { // If the access type is Either, then we do not apply any transition // to this node, but we still update each of its children. @@ -1090,8 +1104,6 @@ impl<'tcx> LocationTree { return Ok(()); }; - let mut entry = args.data.perms.entry(args.idx); - let perm = entry.or_insert(node.default_location_state()); // We know the exact relatedness, so we can actually do precise checks. perm.perform_transition( args.idx, @@ -1115,6 +1127,21 @@ impl<'tcx> LocationTree { }) }, )?; + // If there is no exposed node in this tree that allows this access, then the + // access *must* be foreign. So we check if the root of this tree would allow this + // as a foreign access, and if not, then we can error. + // In practice, all wildcard trees accept foreign accesses, but the main tree does + // not, so this catches UB when none of the nodes in the main tree allows this access. + if !has_valid_exposed + && self + .wildcard_accesses + .get(root) + .unwrap() + .access_relatedness(access_kind, /* only_foreign */ true) + .is_none() + { + return Err(no_valid_exposed_references_error(diagnostics)).into(); + } interp_ok(()) } } diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/wildcard.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/wildcard.rs index 3b55a9e36ea6f..b5ae0ee4c7d31 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/wildcard.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/wildcard.rs @@ -20,6 +20,16 @@ pub enum WildcardAccessLevel { Read, Write, } +impl WildcardAccessLevel { + /// Weather this access kind is allowed at this level. + pub fn allows(self, kind: AccessKind) -> bool { + let required_level = match kind { + AccessKind::Read => Self::Read, + AccessKind::Write => Self::Write, + }; + required_level <= self + } +} /// Where the access happened relative to the current node. #[derive(Clone, Copy, Debug, PartialEq, Eq)] @@ -430,7 +440,7 @@ impl Tree { .map(|p| p.permission()) .unwrap_or_else(|| node.default_location_state().permission()); - let access_type = perm.strongest_allowed_child_access(protected); + let access_type = perm.strongest_allowed_local_access(protected); WildcardState::update_exposure( id, access_type, @@ -480,7 +490,7 @@ impl Tree { perms.get(id).copied().unwrap_or_else(|| node.default_location_state()); perm.permission() - .strongest_allowed_child_access(protected_tags.contains_key(&node.tag)) + .strongest_allowed_local_access(protected_tags.contains_key(&node.tag)) } else { WildcardAccessLevel::None }; diff --git a/src/tools/miri/src/shims/aarch64.rs b/src/tools/miri/src/shims/aarch64.rs index 6e422b4ab716a..595a6595b531d 100644 --- a/src/tools/miri/src/shims/aarch64.rs +++ b/src/tools/miri/src/shims/aarch64.rs @@ -19,20 +19,6 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Prefix should have already been checked. let unprefixed_name = link_name.as_str().strip_prefix("llvm.aarch64.").unwrap(); match unprefixed_name { - "isb" => { - let [arg] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; - let arg = this.read_scalar(arg)?.to_i32()?; - match arg { - // SY ("full system scope") - 15 => { - this.yield_active_thread(); - } - _ => { - throw_unsup_format!("unsupported llvm.aarch64.isb argument {}", arg); - } - } - } - // Used to implement the vpmaxq_u8 function. // Computes the maximum of adjacent pairs; the first half of the output is produced from the // `left` input, the second half of the output from the `right` input. diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 722f34bac02e5..571f5efc9739b 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -813,22 +813,6 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { this, link_name, abi, args, dest, ); } - // FIXME: Move this to an `arm` submodule. - "llvm.arm.hint" if this.tcx.sess.target.arch == Arch::Arm => { - let [arg] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; - let arg = this.read_scalar(arg)?.to_i32()?; - // Note that different arguments might have different target feature requirements. - match arg { - // YIELD - 1 => { - this.expect_target_feature_for_intrinsic(link_name, "v6")?; - this.yield_active_thread(); - } - _ => { - throw_unsup_format!("unsupported llvm.arm.hint argument {}", arg); - } - } - } // Fallback to shims in submodules. _ => { diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index c824147ad4be5..3bc52dddfe8b8 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -307,6 +307,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let res = this.GetFileInformationByHandle(handle, info)?; this.write_scalar(res, dest)?; } + "SetFileInformationByHandle" => { + let [handle, class, info, size] = + this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let res = this.SetFileInformationByHandle(handle, class, info, size)?; + this.write_scalar(res, dest)?; + } + "FlushFileBuffers" => { + let [handle] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; + let res = this.FlushFileBuffers(handle)?; + this.write_scalar(res, dest)?; + } "DeleteFileW" => { let [file_name] = this.check_shim_sig_lenient(abi, sys_conv, link_name, args)?; let res = this.DeleteFileW(file_name)?; diff --git a/src/tools/miri/src/shims/windows/fs.rs b/src/tools/miri/src/shims/windows/fs.rs index ad22df2425af9..e5a98e86d6453 100644 --- a/src/tools/miri/src/shims/windows/fs.rs +++ b/src/tools/miri/src/shims/windows/fs.rs @@ -5,6 +5,7 @@ use std::path::PathBuf; use std::time::SystemTime; use bitflags::bitflags; +use rustc_abi::Size; use rustc_target::spec::Os; use crate::shims::files::{FdId, FileDescription, FileHandle}; @@ -372,6 +373,123 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { interp_ok(this.eval_windows("c", "TRUE")) } + fn SetFileInformationByHandle( + &mut self, + file: &OpTy<'tcx>, // HANDLE + class: &OpTy<'tcx>, // FILE_INFO_BY_HANDLE_CLASS + file_information: &OpTy<'tcx>, // LPVOID + buffer_size: &OpTy<'tcx>, // DWORD + ) -> InterpResult<'tcx, Scalar> { + // ^ Returns BOOL (i32 on Windows) + let this = self.eval_context_mut(); + this.assert_target_os(Os::Windows, "SetFileInformationByHandle"); + this.check_no_isolation("`SetFileInformationByHandle`")?; + + let class = this.read_scalar(class)?.to_u32()?; + let buffer_size = this.read_scalar(buffer_size)?.to_u32()?; + let file_information = this.read_pointer(file_information)?; + this.check_ptr_access( + file_information, + Size::from_bytes(buffer_size), + CheckInAllocMsg::MemoryAccess, + )?; + + let file = this.read_handle(file, "SetFileInformationByHandle")?; + let Handle::File(fd_num) = file else { this.invalid_handle("SetFileInformationByHandle")? }; + let Some(desc) = this.machine.fds.get(fd_num) else { + this.invalid_handle("SetFileInformationByHandle")? + }; + let file = desc.downcast::().ok_or_else(|| { + err_unsup_format!( + "`SetFileInformationByHandle` is only supported on file-backed file descriptors" + ) + })?; + + if class == this.eval_windows_u32("c", "FileEndOfFileInfo") { + let place = this + .ptr_to_mplace(file_information, this.windows_ty_layout("FILE_END_OF_FILE_INFO")); + let new_len = + this.read_scalar(&this.project_field_named(&place, "EndOfFile")?)?.to_i64()?; + match file.file.set_len(new_len.try_into().unwrap()) { + Ok(_) => interp_ok(this.eval_windows("c", "TRUE")), + Err(e) => { + this.set_last_error(e)?; + interp_ok(this.eval_windows("c", "FALSE")) + } + } + } else if class == this.eval_windows_u32("c", "FileAllocationInfo") { + // On Windows, files are somewhat similar to a `Vec` in that they have a separate + // "length" (called "EOF position") and "capacity" (called "allocation size"). + // Growing the allocation size is largely a performance hint which we can + // ignore -- it can also be directly queried, but we currently do not support that. + // So we only need to do something if this operation shrinks the allocation size + // so far that it affects the EOF position. + let place = this + .ptr_to_mplace(file_information, this.windows_ty_layout("FILE_ALLOCATION_INFO")); + let new_alloc_size: u64 = this + .read_scalar(&this.project_field_named(&place, "AllocationSize")?)? + .to_i64()? + .try_into() + .unwrap(); + let old_len = match file.file.metadata() { + Ok(m) => m.len(), + Err(e) => { + this.set_last_error(e)?; + return interp_ok(this.eval_windows("c", "FALSE")); + } + }; + if new_alloc_size < old_len { + match file.file.set_len(new_alloc_size) { + Ok(_) => interp_ok(this.eval_windows("c", "TRUE")), + Err(e) => { + this.set_last_error(e)?; + interp_ok(this.eval_windows("c", "FALSE")) + } + } + } else { + interp_ok(this.eval_windows("c", "TRUE")) + } + } else { + throw_unsup_format!( + "SetFileInformationByHandle: Unsupported `FileInformationClass` value {}", + class + ) + } + } + + fn FlushFileBuffers( + &mut self, + file: &OpTy<'tcx>, // HANDLE + ) -> InterpResult<'tcx, Scalar> { + // ^ returns BOOL (i32 on Windows) + let this = self.eval_context_mut(); + this.assert_target_os(Os::Windows, "FlushFileBuffers"); + + let file = this.read_handle(file, "FlushFileBuffers")?; + let Handle::File(fd_num) = file else { this.invalid_handle("FlushFileBuffers")? }; + let Some(desc) = this.machine.fds.get(fd_num) else { + this.invalid_handle("FlushFileBuffers")? + }; + let file = desc.downcast::().ok_or_else(|| { + err_unsup_format!( + "`FlushFileBuffers` is only supported on file-backed file descriptors" + ) + })?; + + if !file.writable { + this.set_last_error(IoError::WindowsError("ERROR_ACCESS_DENIED"))?; + return interp_ok(this.eval_windows("c", "FALSE")); + } + + match file.file.sync_all() { + Ok(_) => interp_ok(this.eval_windows("c", "TRUE")), + Err(e) => { + this.set_last_error(e)?; + interp_ok(this.eval_windows("c", "FALSE")) + } + } + } + fn DeleteFileW( &mut self, file_name: &OpTy<'tcx>, // LPCWSTR diff --git a/src/tools/miri/src/shims/x86/avx2.rs b/src/tools/miri/src/shims/x86/avx2.rs index 8fe225c494d5b..b089a0249bd79 100644 --- a/src/tools/miri/src/shims/x86/avx2.rs +++ b/src/tools/miri/src/shims/x86/avx2.rs @@ -6,7 +6,7 @@ use rustc_target::callconv::FnAbi; use super::{ ShiftOp, horizontal_bin_op, mpsadbw, packssdw, packsswb, packusdw, packuswb, permute, pmaddbw, - pmulhrsw, psadbw, psign, shift_simd_by_scalar, + pmulhrsw, psadbw, pshufb, psign, shift_simd_by_scalar, }; use crate::*; @@ -189,28 +189,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let [left, right] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; - let (left, left_len) = this.project_to_simd(left)?; - let (right, right_len) = this.project_to_simd(right)?; - let (dest, dest_len) = this.project_to_simd(dest)?; - - assert_eq!(dest_len, left_len); - assert_eq!(dest_len, right_len); - - for i in 0..dest_len { - let right = this.read_scalar(&this.project_index(&right, i)?)?.to_u8()?; - let dest = this.project_index(&dest, i)?; - - let res = if right & 0x80 == 0 { - // Shuffle each 128-bit (16-byte) block independently. - let j = u64::from(right % 16).strict_add(i & !15); - this.read_scalar(&this.project_index(&left, j)?)? - } else { - // If the highest bit in `right` is 1, write zero. - Scalar::from_u8(0) - }; - - this.write_scalar(res, &dest)?; - } + pshufb(this, left, right, dest)?; } // Used to implement the _mm256_sign_epi{8,16,32} functions. // Negates elements from `left` when the corresponding element in diff --git a/src/tools/miri/src/shims/x86/avx512.rs b/src/tools/miri/src/shims/x86/avx512.rs index 9b43aad96e5ca..a886f5622ceda 100644 --- a/src/tools/miri/src/shims/x86/avx512.rs +++ b/src/tools/miri/src/shims/x86/avx512.rs @@ -3,7 +3,7 @@ use rustc_middle::ty::Ty; use rustc_span::Symbol; use rustc_target::callconv::FnAbi; -use super::{permute, pmaddbw, psadbw}; +use super::{permute, pmaddbw, psadbw, pshufb}; use crate::*; impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} @@ -102,6 +102,13 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { permute(this, left, right, dest)?; } + // Used to implement the _mm512_shuffle_epi8 intrinsic. + "pshuf.b.512" => { + let [left, right] = + this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; + + pshufb(this, left, right, dest)?; + } _ => return interp_ok(EmulateItemResult::NotSupported), } interp_ok(EmulateItemResult::NeedsReturn) diff --git a/src/tools/miri/src/shims/x86/mod.rs b/src/tools/miri/src/shims/x86/mod.rs index 40dcd7ac1c2de..a5164cc87ab4d 100644 --- a/src/tools/miri/src/shims/x86/mod.rs +++ b/src/tools/miri/src/shims/x86/mod.rs @@ -1155,6 +1155,51 @@ fn pclmulqdq<'tcx>( interp_ok(()) } +/// Shuffles bytes from `left` using `right` as pattern. Each 16-byte block is shuffled independently. +/// +/// `left` and `right` are both vectors of type `len` x i8. +/// +/// If the highest bit of a byte in `right` is not set, the corresponding byte in `dest` is taken +/// from the current 16-byte block of `left` at the position indicated by the lowest 4 bits of this +/// byte in `right`. If the highest bit of a byte in `right` is set, the corresponding byte in +/// `dest` is set to `0`. +/// +/// +/// +/// +fn pshufb<'tcx>( + ecx: &mut crate::MiriInterpCx<'tcx>, + left: &OpTy<'tcx>, + right: &OpTy<'tcx>, + dest: &MPlaceTy<'tcx>, +) -> InterpResult<'tcx, ()> { + let (left, left_len) = ecx.project_to_simd(left)?; + let (right, right_len) = ecx.project_to_simd(right)?; + let (dest, dest_len) = ecx.project_to_simd(dest)?; + + assert_eq!(dest_len, left_len); + assert_eq!(dest_len, right_len); + + for i in 0..dest_len { + let right = ecx.read_scalar(&ecx.project_index(&right, i)?)?.to_u8()?; + let dest = ecx.project_index(&dest, i)?; + + let res = if right & 0x80 == 0 { + // Shuffle each 128-bit (16-byte) block independently. + let block_offset = i & !15; // round down to previous multiple of 16 + let j = block_offset.strict_add((right % 16).into()); + ecx.read_scalar(&ecx.project_index(&left, j)?)? + } else { + // If the highest bit in `right` is 1, write zero. + Scalar::from_u8(0) + }; + + ecx.write_scalar(res, &dest)?; + } + + interp_ok(()) +} + /// Packs two N-bit integer vectors to a single N/2-bit integers. /// /// The conversion from N-bit to N/2-bit should be provided by `f`. diff --git a/src/tools/miri/src/shims/x86/ssse3.rs b/src/tools/miri/src/shims/x86/ssse3.rs index 56fc63ce14733..b01a8795b4d13 100644 --- a/src/tools/miri/src/shims/x86/ssse3.rs +++ b/src/tools/miri/src/shims/x86/ssse3.rs @@ -4,7 +4,7 @@ use rustc_middle::ty::Ty; use rustc_span::Symbol; use rustc_target::callconv::FnAbi; -use super::{horizontal_bin_op, pmaddbw, pmulhrsw, psign}; +use super::{horizontal_bin_op, pmaddbw, pmulhrsw, pshufb, psign}; use crate::*; impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} @@ -29,27 +29,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let [left, right] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?; - let (left, left_len) = this.project_to_simd(left)?; - let (right, right_len) = this.project_to_simd(right)?; - let (dest, dest_len) = this.project_to_simd(dest)?; - - assert_eq!(dest_len, left_len); - assert_eq!(dest_len, right_len); - - for i in 0..dest_len { - let right = this.read_scalar(&this.project_index(&right, i)?)?.to_u8()?; - let dest = this.project_index(&dest, i)?; - - let res = if right & 0x80 == 0 { - let j = right % 16; // index wraps around - this.read_scalar(&this.project_index(&left, j.into())?)? - } else { - // If the highest bit in `right` is 1, write zero. - Scalar::from_u8(0) - }; - - this.write_scalar(res, &dest)?; - } + pshufb(this, left, right, dest)?; } // Used to implement the _mm_h{adds,subs}_epi16 functions. // Horizontally add / subtract with saturation adjacent 16-bit diff --git a/src/tools/miri/tests/fail/closures/deref-in-pattern.rs b/src/tools/miri/tests/fail/closures/deref-in-pattern.rs index c729469c78b0e..27915a9a489b5 100644 --- a/src/tools/miri/tests/fail/closures/deref-in-pattern.rs +++ b/src/tools/miri/tests/fail/closures/deref-in-pattern.rs @@ -9,12 +9,13 @@ fn main() { // the inner reference is dangling let x: &&u32 = unsafe { let x: u32 = 42; - &&* &raw const x + &&*&raw const x }; - let _ = || { //~ ERROR: encountered a dangling reference + //~v ERROR: encountered a dangling reference + let _ = || { match x { - &&_y => {}, + &&_y => {} } }; } diff --git a/src/tools/miri/tests/fail/closures/deref-in-pattern.stderr b/src/tools/miri/tests/fail/closures/deref-in-pattern.stderr index 1264f4e3fb95a..fae2d286c4876 100644 --- a/src/tools/miri/tests/fail/closures/deref-in-pattern.stderr +++ b/src/tools/miri/tests/fail/closures/deref-in-pattern.stderr @@ -4,7 +4,7 @@ error: Undefined Behavior: constructing invalid value: encountered a dangling re LL | let _ = || { | _____________^ LL | | match x { -LL | | &&_y => {}, +LL | | &&_y => {} LL | | } LL | | }; | |_____^ Undefined Behavior occurred here diff --git a/src/tools/miri/tests/fail/closures/partial-pattern.rs b/src/tools/miri/tests/fail/closures/partial-pattern.rs index cb64462081802..f70fe096fd80a 100644 --- a/src/tools/miri/tests/fail/closures/partial-pattern.rs +++ b/src/tools/miri/tests/fail/closures/partial-pattern.rs @@ -15,14 +15,15 @@ fn main() { let x: &(&u32, &u32) = unsafe { let a = 21; let b = 37; - let ra = &* &raw const a; - let rb = &* &raw const b; + let ra = &*&raw const a; + let rb = &*&raw const b; &(ra, rb) }; - let _ = || { //~ ERROR: encountered a dangling reference + //~v ERROR: encountered a dangling reference + let _ = || { match x { - (&_y, _) => {}, + (&_y, _) => {} } }; } diff --git a/src/tools/miri/tests/fail/closures/partial-pattern.stderr b/src/tools/miri/tests/fail/closures/partial-pattern.stderr index 5b7ee35ecc736..8dea4d4d8c64e 100644 --- a/src/tools/miri/tests/fail/closures/partial-pattern.stderr +++ b/src/tools/miri/tests/fail/closures/partial-pattern.stderr @@ -4,7 +4,7 @@ error: Undefined Behavior: constructing invalid value: encountered a dangling re LL | let _ = || { | _____________^ LL | | match x { -LL | | (&_y, _) => {}, +LL | | (&_y, _) => {} LL | | } LL | | }; | |_____^ Undefined Behavior occurred here diff --git a/src/tools/miri/tests/fail/closures/uninhabited-variant.rs b/src/tools/miri/tests/fail/closures/uninhabited-variant.rs index 9c50fdc13a35f..0a2dfe9a7f645 100644 --- a/src/tools/miri/tests/fail/closures/uninhabited-variant.rs +++ b/src/tools/miri/tests/fail/closures/uninhabited-variant.rs @@ -5,9 +5,9 @@ #[repr(C)] #[allow(dead_code)] enum E { - V0, // discriminant: 0 - V1, // 1 - V2(!), // 2 + V0, // discriminant: 0 + V1, // 1 + V2(!), // 2 } fn main() { @@ -20,7 +20,8 @@ fn main() { // After rust-lang/rust#138961, constructing the closure performs a reborrow of r. // Nevertheless, the discriminant is only actually inspected when the closure // is called. - match r { //~ ERROR: read discriminant of an uninhabited enum variant + match r { + //~^ ERROR: read discriminant of an uninhabited enum variant E::V0 => {} E::V1 => {} E::V2(_) => {} diff --git a/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_main_invalid_exposed2.rs b/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_main_invalid_exposed2.rs new file mode 100644 index 0000000000000..ca430cad16704 --- /dev/null +++ b/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_main_invalid_exposed2.rs @@ -0,0 +1,48 @@ +//@compile-flags: -Zmiri-tree-borrows -Zmiri-permissive-provenance +use std::cell::Cell; + +/// Checks how accesses from one subtree affect other subtrees. +/// This test checks that an access from a subtree performs a +/// wildcard access on all earlier trees, and that local +/// accesses are treated as access errors for tags that are +/// larger than the root of the accessed subtree. +/// This tests the case were we have multiple exposed nodes on +/// the main tree that are invalid because their tag is too large. +pub fn main() { + let mut x: u32 = 42; + + let ptr_base = &mut x as *mut u32; + let ref1 = unsafe { &mut *ptr_base }; + let int1 = ref1 as *mut u32 as usize; + let wild = int1 as *mut u32; + + // Activates ref1. + *ref1 = 4; + + let ref2 = unsafe { &mut *wild }; + + // Freezes ref1. + let ref3 = unsafe { &mut *(ptr_base as *mut Cell) }; + let _int3 = ref3 as *mut Cell as usize; + let ref4 = unsafe { &mut *(ptr_base as *mut Cell) }; + let _int4 = ref4 as *mut Cell as usize; + + // ┌──────────────┐ + // │ │ + // │ptr_base(Act) ├───────────┬──────────────────┐ * + // │ │ │ │ │ + // └──────┬───────┘ │ │ │ + // │ │ │ │ + // │ │ │ │ + // ▼ ▼ ▼ ▼ + // ┌─────────────┐ ┌────────────┐ ┌────────────┐ ┌───────────┐ + // │ │ │ │ │ │ │ │ + // │ ref1(Frz)* │ │ ref3(ReIM)*│ │ ref4(ReIM)*│ │ ref2(Res) │ + // │ │ │ │ │ │ │ │ + // └─────────────┘ └────────────┘ └────────────┘ └───────────┘ + + // Performs a wildcard access on the main root. However, as there are + // no exposed tags with write permissions and a tag smaller than ref2 + // this access fails. + *ref2 = 13; //~ ERROR: /write access through .* is forbidden/ +} diff --git a/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_main_invalid_exposed2.stderr b/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_main_invalid_exposed2.stderr new file mode 100644 index 0000000000000..1f7a3d61829c7 --- /dev/null +++ b/src/tools/miri/tests/fail/tree_borrows/wildcard/cross_tree_update_main_invalid_exposed2.stderr @@ -0,0 +1,14 @@ +error: Undefined Behavior: write access through at ALLOC[0x0] is forbidden + --> tests/fail/tree_borrows/wildcard/cross_tree_update_main_invalid_exposed2.rs:LL:CC + | +LL | *ref2 = 13; + | ^^^^^^^^^^ Undefined Behavior occurred here + | + = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental + = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information + = help: there are no exposed tags which may perform this access here + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/genmc/fail/shims/exit.stderr b/src/tools/miri/tests/genmc/fail/shims/exit.stderr index dead5faaa514d..f27860b82fe73 100644 --- a/src/tools/miri/tests/genmc/fail/shims/exit.stderr +++ b/src/tools/miri/tests/genmc/fail/shims/exit.stderr @@ -1,114 +1,4 @@ Running GenMC Verification... -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/thread/mod.rs:LL:CC - | -LL | match COUNTER.compare_exchange_weak(last, id, Ordering::Relaxed, Ordering::Relaxed) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside `std::thread::ThreadId::new` at RUSTLIB/std/src/thread/mod.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/thread/current.rs:LL:CC - = note: inside `std::thread::current::id::get_or_init` at RUSTLIB/std/src/thread/current.rs:LL:CC - = note: inside `std::thread::current_id` at RUSTLIB/std/src/thread/current.rs:LL:CC - = note: inside `std::rt::init` at RUSTLIB/std/src/rt.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/rt.rs:LL:CC - = note: inside `std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panicking.rs:LL:CC - = note: inside `std::panicking::catch_unwind::` at RUSTLIB/std/src/panicking.rs:LL:CC - = note: inside `std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>` at RUSTLIB/std/src/panic.rs:LL:CC - = note: inside `std::rt::lang_start_internal` at RUSTLIB/std/src/rt.rs:LL:CC - = note: inside `std::rt::lang_start::<()>` at RUSTLIB/std/src/rt.rs:LL:CC - -warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. - --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC - | -LL | || self - | ________________^ -LL | | .state -LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) - | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside `std::sys::sync::PLATFORM::futex::RwLock::read` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC - = note: inside `std::sync::RwLock::<()>::read` at RUSTLIB/std/src/sync/poison/rwlock.rs:LL:CC - = note: inside `std::sys::env::PLATFORM::env_read_lock` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC - = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr_stack::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC - = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC - = note: inside `std::sys::env::PLATFORM::getenv` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC - = note: inside `std::env::_var_os` at RUSTLIB/std/src/env.rs:LL:CC - = note: inside `std::env::var_os::<&str>` at RUSTLIB/std/src/env.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC -note: inside `main` - --> tests/genmc/fail/shims/exit.rs:LL:CC - | -LL | / std::thread::spawn(|| { -LL | | unsafe { std::hint::unreachable_unchecked() }; -LL | | }); - | |______^ - -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC - | -LL | || self - | ________________^ -LL | | .state -LL | | .compare_exchange_weak(state, state + READ_LOCKED, Acquire, Relaxed) - | |____________________________________________________________________________________^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside `std::sys::sync::PLATFORM::futex::RwLock::read` at RUSTLIB/std/src/sys/sync/PLATFORM/futex.rs:LL:CC - = note: inside `std::sync::RwLock::<()>::read` at RUSTLIB/std/src/sync/poison/rwlock.rs:LL:CC - = note: inside `std::sys::env::PLATFORM::env_read_lock` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC - = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr_stack::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC - = note: inside `std::sys::pal::PLATFORM::small_c_string::run_with_cstr::>` at RUSTLIB/std/src/sys/pal/PLATFORM/small_c_string.rs:LL:CC - = note: inside `std::sys::env::PLATFORM::getenv` at RUSTLIB/std/src/sys/env/PLATFORM.rs:LL:CC - = note: inside `std::env::_var_os` at RUSTLIB/std/src/env.rs:LL:CC - = note: inside `std::env::var_os::<&str>` at RUSTLIB/std/src/env.rs:LL:CC - = note: inside closure at RUSTLIB/std/src/thread/mod.rs:LL:CC -note: inside `main` - --> tests/genmc/fail/shims/exit.rs:LL:CC - | -LL | / std::thread::spawn(|| { -LL | | unsafe { std::hint::unreachable_unchecked() }; -LL | | }); - | |______^ - -warning: GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures. - --> RUSTLIB/std/src/rt.rs:LL:CC - | -LL | / CLEANUP.call_once(|| unsafe { -LL | | // Flush stdout and disable buffering. -LL | | crate::io::cleanup(); -... | -LL | | }); - | |______^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside `std::rt::cleanup` at RUSTLIB/std/src/rt.rs:LL:CC - = note: inside `std::process::exit` at RUSTLIB/std/src/process.rs:LL:CC -note: inside `main` - --> tests/genmc/fail/shims/exit.rs:LL:CC - | -LL | std::process::exit(0); - | ^^^^^^^^^^^^^^^^^^^^^ - -warning: GenMC currently does not model the failure ordering for `compare_exchange`. Due to success ordering 'Acquire', the failure ordering 'Relaxed' is treated like 'Acquire'. Miri with GenMC might miss bugs related to this memory access. - --> RUSTLIB/std/src/sys/exit_guard.rs:LL:CC - | -LL | match EXITING_THREAD_ID.compare_exchange(ptr::null_mut(), this_thread_id, Acquire, Relaxed) { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code - | - = note: BACKTRACE: - = note: inside `std::sys::exit_guard::unique_thread_exit` at RUSTLIB/std/src/sys/exit_guard.rs:LL:CC - = note: inside `std::sys::pal::PLATFORM::os::exit` at RUSTLIB/std/src/sys/pal/PLATFORM/os.rs:LL:CC - = note: inside `std::process::exit` at RUSTLIB/std/src/process.rs:LL:CC -note: inside `main` - --> tests/genmc/fail/shims/exit.rs:LL:CC - | -LL | std::process::exit(0); - | ^^^^^^^^^^^^^^^^^^^^^ - error: Undefined Behavior: entering unreachable code --> tests/genmc/fail/shims/exit.rs:LL:CC | @@ -122,5 +12,5 @@ note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report -error: aborting due to 1 previous error; 5 warnings emitted +error: aborting due to 1 previous error diff --git a/src/tools/miri/tests/panic/mir-validation.rs b/src/tools/miri/tests/panic/mir-validation.rs index 11c4e395920c0..4c863c5a9de86 100644 --- a/src/tools/miri/tests/panic/mir-validation.rs +++ b/src/tools/miri/tests/panic/mir-validation.rs @@ -5,6 +5,8 @@ //@normalize-stderr-test: "\n +\[\.\.\. omitted [0-9]+ frames? \.\.\.\].*" -> "" //@normalize-stderr-test: "\n[ =]*note:.*" -> "" //@normalize-stderr-test: "DefId\([^()]*\)" -> "DefId" +// Paths differ between bootstrap and stand-alone Miri runs, normalize them to be the same +//@normalize-stderr-test: "/rustc-dev/[^/]*/" -> "" // Somehow on rustc Windows CI, the "Miri caused an ICE" message is not shown // and we don't even get a regular panic; rustc aborts with a different exit code instead. //@ignore-host: windows diff --git a/src/tools/miri/tests/pass-dep/shims/windows-fs.rs b/src/tools/miri/tests/pass-dep/shims/windows-fs.rs index 7b756603d929b..91639c5023252 100644 --- a/src/tools/miri/tests/pass-dep/shims/windows-fs.rs +++ b/src/tools/miri/tests/pass-dep/shims/windows-fs.rs @@ -19,9 +19,11 @@ use windows_sys::Win32::Foundation::{ }; use windows_sys::Win32::Storage::FileSystem::{ BY_HANDLE_FILE_INFORMATION, CREATE_ALWAYS, CREATE_NEW, CreateFileW, DeleteFileW, - FILE_ATTRIBUTE_DIRECTORY, FILE_ATTRIBUTE_NORMAL, FILE_BEGIN, FILE_CURRENT, - FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_OPEN_REPARSE_POINT, FILE_SHARE_DELETE, FILE_SHARE_READ, - FILE_SHARE_WRITE, GetFileInformationByHandle, OPEN_ALWAYS, OPEN_EXISTING, SetFilePointerEx, + FILE_ALLOCATION_INFO, FILE_ATTRIBUTE_DIRECTORY, FILE_ATTRIBUTE_NORMAL, FILE_BEGIN, + FILE_CURRENT, FILE_END_OF_FILE_INFO, FILE_FLAG_BACKUP_SEMANTICS, FILE_FLAG_OPEN_REPARSE_POINT, + FILE_SHARE_DELETE, FILE_SHARE_READ, FILE_SHARE_WRITE, FileAllocationInfo, FileEndOfFileInfo, + FlushFileBuffers, GetFileInformationByHandle, OPEN_ALWAYS, OPEN_EXISTING, + SetFileInformationByHandle, SetFilePointerEx, }; use windows_sys::Win32::System::IO::IO_STATUS_BLOCK; use windows_sys::Win32::System::Threading::GetCurrentProcess; @@ -37,7 +39,9 @@ fn main() { test_ntstatus_to_dos(); test_file_read_write(); test_file_seek(); + test_set_file_info(); test_dup_handle(); + test_flush_buffers(); } } @@ -275,6 +279,32 @@ unsafe fn test_file_read_write() { assert_eq!(GetLastError(), 1234); } +unsafe fn test_set_file_info() { + let temp = utils::tmp().join("test_set_file.txt"); + let mut file = fs::File::create(&temp).unwrap(); + let handle = file.as_raw_handle(); + + let info = FILE_END_OF_FILE_INFO { EndOfFile: 20 }; + let res = SetFileInformationByHandle( + handle, + FileEndOfFileInfo, + ptr::from_ref(&info).cast(), + size_of::().try_into().unwrap(), + ); + assert!(res != 0); + assert_eq!(file.seek(SeekFrom::End(0)).unwrap(), 20); + + let info = FILE_ALLOCATION_INFO { AllocationSize: 0 }; + let res = SetFileInformationByHandle( + handle, + FileAllocationInfo, + ptr::from_ref(&info).cast(), + size_of::().try_into().unwrap(), + ); + assert!(res != 0); + assert_eq!(file.metadata().unwrap().len(), 0); +} + unsafe fn test_dup_handle() { let temp = utils::tmp().join("test_dup.txt"); @@ -333,6 +363,19 @@ unsafe fn test_file_seek() { assert_eq!(pos, 5); } +unsafe fn test_flush_buffers() { + let temp = utils::tmp().join("test_flush_buffers.txt"); + let file = fs::File::options().create(true).write(true).read(true).open(&temp).unwrap(); + if FlushFileBuffers(file.as_raw_handle()) == 0 { + panic!("Failed to flush buffers"); + } + + let file = fs::File::options().read(true).open(&temp).unwrap(); + if FlushFileBuffers(file.as_raw_handle()) != 0 { + panic!("Successfully flushed buffers on read-only file"); + } +} + fn to_wide_cstr(path: &Path) -> Vec { let mut raw_path = path.as_os_str().encode_wide().collect::>(); raw_path.extend([0, 0]); diff --git a/src/tools/miri/tests/pass/shims/fs.rs b/src/tools/miri/tests/pass/shims/fs.rs index 43fbf6b085f75..648c90b5dd971 100644 --- a/src/tools/miri/tests/pass/shims/fs.rs +++ b/src/tools/miri/tests/pass/shims/fs.rs @@ -28,10 +28,10 @@ fn main() { test_errors(); test_from_raw_os_error(); test_file_clone(); + test_file_set_len(); + test_file_sync(); // Windows file handling is very incomplete. if cfg!(not(windows)) { - test_file_set_len(); - test_file_sync(); test_rename(); test_directory(); test_canonicalize(); @@ -77,6 +77,9 @@ fn test_file() { // However, writing 0 bytes can succeed or fail. let _ignore = file.write(&[]); + // Test calling File::create on an existing file, since that uses a different code path + File::create(&path).unwrap(); + // Removing file should succeed. remove_file(&path).unwrap(); } @@ -87,7 +90,6 @@ fn test_file_partial_reads_writes() { // Ensure we sometimes do incomplete writes. check_nondet(|| { - let _ = remove_file(&path1); // FIXME(win, issue #4483): errors if the file already exists let mut file = File::create(&path1).unwrap(); file.write(&[0; 4]).unwrap() == 4 }); @@ -210,7 +212,12 @@ fn test_file_set_len() { // Can't use set_len on a file not opened for writing let file = OpenOptions::new().read(true).open(&path).unwrap(); - assert_eq!(ErrorKind::InvalidInput, file.set_len(14).unwrap_err().kind()); + // Due to https://github.com/rust-lang/miri/issues/4457, we have to assume the failure could + // be either of the Windows or Unix kind, no matter which platform we're on. + assert!( + [ErrorKind::PermissionDenied, ErrorKind::InvalidInput] + .contains(&file.set_len(14).unwrap_err().kind()) + ); remove_file(&path).unwrap(); } @@ -224,10 +231,16 @@ fn test_file_sync() { file.sync_data().unwrap(); file.sync_all().unwrap(); - // Test that we can call sync_data and sync_all on a file opened for reading. + // Test that we can call sync_data and sync_all on a file opened for reading on unix, but not + // on Windows let file = File::open(&path).unwrap(); - file.sync_data().unwrap(); - file.sync_all().unwrap(); + if cfg!(unix) { + file.sync_data().unwrap(); + file.sync_all().unwrap(); + } else { + file.sync_data().unwrap_err(); + file.sync_all().unwrap_err(); + } remove_file(&path).unwrap(); } diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs index e778567b483f8..f95429d59ebec 100644 --- a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs +++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-avx512.rs @@ -143,6 +143,30 @@ unsafe fn test_avx512() { assert_eq_m512i(r, e); } test_mm512_permutexvar_epi32(); + + #[target_feature(enable = "avx512bw")] + unsafe fn test_mm512_shuffle_epi8() { + #[rustfmt::skip] + let a = _mm512_set_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63); + #[rustfmt::skip] + let b = _mm512_set_epi8(-1, 127, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + -1, 127, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + -1, 127, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + -1, 127, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1); + let r = _mm512_shuffle_epi8(a, b); + // `_mm512_set_epi8` sets the bytes in inverse order (?!?), so the indices in `b` seem to + // index from the *back* of the corresponding 16-byte block in `a`. + #[rustfmt::skip] + let e = _mm512_set_epi8(0, 0, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, + 0, 16, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, + 0, 32, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, + 0, 48, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62); + assert_eq_m512i(r, e); + } + test_mm512_shuffle_epi8(); } // Some of the constants in the tests below are just bit patterns. They should not