diff --git a/src/tools/miri/src/concurrency/cpu_affinity.rs b/src/tools/miri/src/concurrency/cpu_affinity.rs index 085900ac3aa6..8df26d718bf6 100644 --- a/src/tools/miri/src/concurrency/cpu_affinity.rs +++ b/src/tools/miri/src/concurrency/cpu_affinity.rs @@ -1,6 +1,8 @@ -use crate::bug; +use rustc_middle::ty::layout::LayoutOf; use rustc_target::abi::Endian; +use crate::*; + /// The maximum number of CPUs supported by miri. /// /// This value is compatible with the libc `CPU_SETSIZE` constant and corresponds to the number @@ -19,41 +21,34 @@ pub(crate) struct CpuAffinityMask([u8; Self::CPU_MASK_BYTES]); impl CpuAffinityMask { pub(crate) const CPU_MASK_BYTES: usize = MAX_CPUS / 8; - pub fn new(target: &rustc_target::spec::Target, cpu_count: u32) -> Self { + pub fn new<'tcx>(cx: &impl LayoutOf<'tcx>, cpu_count: u32) -> Self { let mut this = Self([0; Self::CPU_MASK_BYTES]); // the default affinity mask includes only the available CPUs for i in 0..cpu_count as usize { - this.set(target, i); + this.set(cx, i); } this } - pub fn chunk_size(target: &rustc_target::spec::Target) -> u64 { - // The actual representation of the CpuAffinityMask is [c_ulong; _], in practice either - // - // - [u32; 32] on 32-bit platforms - // - [u64; 16] everywhere else - - // FIXME: this should be `size_of::()` - u64::from(target.pointer_width / 8) + pub fn chunk_size<'tcx>(cx: &impl LayoutOf<'tcx>) -> u64 { + // The actual representation of the CpuAffinityMask is [c_ulong; _]. + let ulong = helpers::path_ty_layout(cx, &["core", "ffi", "c_ulong"]); + ulong.size.bytes() } - fn set(&mut self, target: &rustc_target::spec::Target, cpu: usize) { + fn set<'tcx>(&mut self, cx: &impl LayoutOf<'tcx>, cpu: usize) { // we silently ignore CPUs that are out of bounds. This matches the behavior of // `sched_setaffinity` with a mask that specifies more than `CPU_SETSIZE` CPUs. if cpu >= MAX_CPUS { return; } - // The actual representation of the CpuAffinityMask is [c_ulong; _], in practice either - // - // - [u32; 32] on 32-bit platforms - // - [u64; 16] everywhere else - // + // The actual representation of the CpuAffinityMask is [c_ulong; _]. // Within the array elements, we need to use the endianness of the target. - match Self::chunk_size(target) { + let target = &cx.tcx().sess.target; + match Self::chunk_size(cx) { 4 => { let start = cpu / 32 * 4; // first byte of the correct u32 let chunk = self.0[start..].first_chunk_mut::<4>().unwrap(); @@ -72,7 +67,7 @@ impl CpuAffinityMask { Endian::Big => (u64::from_be_bytes(*chunk) | 1 << offset).to_be_bytes(), }; } - other => bug!("other chunk sizes are not supported: {other}"), + other => bug!("chunk size not supported: {other}"), }; } @@ -80,13 +75,13 @@ impl CpuAffinityMask { self.0.as_slice() } - pub fn from_array( - target: &rustc_target::spec::Target, + pub fn from_array<'tcx>( + cx: &impl LayoutOf<'tcx>, cpu_count: u32, bytes: [u8; Self::CPU_MASK_BYTES], ) -> Option { // mask by what CPUs are actually available - let default = Self::new(target, cpu_count); + let default = Self::new(cx, cpu_count); let masked = std::array::from_fn(|i| bytes[i] & default.0[i]); // at least one thread must be set for the input to be valid diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs index 9142b8b5fdbc..2184a4426c8d 100644 --- a/src/tools/miri/src/eval.rs +++ b/src/tools/miri/src/eval.rs @@ -282,7 +282,8 @@ pub fn create_ecx<'tcx>( })?; // Make sure we have MIR. We check MIR for some stable monomorphic function in libcore. - let sentinel = ecx.try_resolve_path(&["core", "ascii", "escape_default"], Namespace::ValueNS); + let sentinel = + helpers::try_resolve_path(tcx, &["core", "ascii", "escape_default"], Namespace::ValueNS); if !matches!(sentinel, Some(s) if tcx.is_mir_available(s.def.def_id())) { tcx.dcx().fatal( "the current sysroot was built without `-Zalways-encode-mir`, or libcore seems missing. \ diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs index 590e8984e990..ba094c988e5a 100644 --- a/src/tools/miri/src/helpers.rs +++ b/src/tools/miri/src/helpers.rs @@ -18,6 +18,7 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags; use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::middle::exported_symbols::ExportedSymbol; use rustc_middle::mir; +use rustc_middle::ty::layout::MaybeResult; use rustc_middle::ty::{ self, layout::{LayoutOf, TyAndLayout}, @@ -159,6 +160,35 @@ fn try_resolve_did(tcx: TyCtxt<'_>, path: &[&str], namespace: Option) None } +/// Gets an instance for a path; fails gracefully if the path does not exist. +pub fn try_resolve_path<'tcx>( + tcx: TyCtxt<'tcx>, + path: &[&str], + namespace: Namespace, +) -> Option> { + let did = try_resolve_did(tcx, path, Some(namespace))?; + Some(ty::Instance::mono(tcx, did)) +} + +/// Gets an instance for a path. +#[track_caller] +pub fn resolve_path<'tcx>( + tcx: TyCtxt<'tcx>, + path: &[&str], + namespace: Namespace, +) -> ty::Instance<'tcx> { + try_resolve_path(tcx, path, namespace) + .unwrap_or_else(|| panic!("failed to find required Rust item: {path:?}")) +} + +/// Gets the layout of a type at a path. +#[track_caller] +pub fn path_ty_layout<'tcx>(cx: &impl LayoutOf<'tcx>, path: &[&str]) -> TyAndLayout<'tcx> { + let ty = + resolve_path(cx.tcx(), path, Namespace::TypeNS).ty(cx.tcx(), ty::ParamEnv::reveal_all()); + cx.layout_of(ty).to_result().ok().unwrap() +} + /// Call `f` for each exported symbol. pub fn iter_exported_symbols<'tcx>( tcx: TyCtxt<'tcx>, @@ -259,23 +289,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { try_resolve_did(*self.eval_context_ref().tcx, path, None).is_some() } - /// Gets an instance for a path; fails gracefully if the path does not exist. - fn try_resolve_path(&self, path: &[&str], namespace: Namespace) -> Option> { - let tcx = self.eval_context_ref().tcx.tcx; - let did = try_resolve_did(tcx, path, Some(namespace))?; - Some(ty::Instance::mono(tcx, did)) - } - - /// Gets an instance for a path. - fn resolve_path(&self, path: &[&str], namespace: Namespace) -> ty::Instance<'tcx> { - self.try_resolve_path(path, namespace) - .unwrap_or_else(|| panic!("failed to find required Rust item: {path:?}")) - } - /// Evaluates the scalar at the specified path. fn eval_path(&self, path: &[&str]) -> OpTy<'tcx> { let this = self.eval_context_ref(); - let instance = this.resolve_path(path, Namespace::ValueNS); + let instance = resolve_path(*this.tcx, path, Namespace::ValueNS); // We don't give a span -- this isn't actually used directly by the program anyway. let const_val = this.eval_global(instance).unwrap_or_else(|err| { panic!("failed to evaluate required Rust item: {path:?}\n{err:?}") @@ -344,19 +361,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "`libc` crate is not reliably available on Windows targets; Miri should not use it there" ); } - let ty = this - .resolve_path(&["libc", name], Namespace::TypeNS) - .ty(*this.tcx, ty::ParamEnv::reveal_all()); - this.layout_of(ty).unwrap() + path_ty_layout(this, &["libc", name]) } /// Helper function to get the `TyAndLayout` of a `windows` type fn windows_ty_layout(&self, name: &str) -> TyAndLayout<'tcx> { let this = self.eval_context_ref(); - let ty = this - .resolve_path(&["std", "sys", "pal", "windows", "c", name], Namespace::TypeNS) - .ty(*this.tcx, ty::ParamEnv::reveal_all()); - this.layout_of(ty).unwrap() + path_ty_layout(this, &["std", "sys", "pal", "windows", "c", name]) } /// Project to the given *named* field (which must be a struct or union type). diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index fee6ab068175..02bfd6ec8150 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -643,10 +643,8 @@ impl<'tcx> MiriMachine<'tcx> { let threads = ThreadManager::default(); let mut thread_cpu_affinity = FxHashMap::default(); if matches!(&*tcx.sess.target.os, "linux" | "freebsd" | "android") { - thread_cpu_affinity.insert( - threads.active_thread(), - CpuAffinityMask::new(&tcx.sess.target, config.num_cpus), - ); + thread_cpu_affinity + .insert(threads.active_thread(), CpuAffinityMask::new(&layout_cx, config.num_cpus)); } MiriMachine { tcx, diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index f5d3e0b536be..f1bae8646d31 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -594,11 +594,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { _ => throw_unsup_format!("`sched_getaffinity` is only supported with a pid of 0 (indicating the current thread)"), }; - // The actual representation of the CpuAffinityMask is [c_ulong; _], in practice either - // - // - [u32; 32] on 32-bit platforms - // - [u64; 16] everywhere else - let chunk_size = CpuAffinityMask::chunk_size(&this.tcx.sess.target); + // The mask is stored in chunks, and the size must be a whole number of chunks. + let chunk_size = CpuAffinityMask::chunk_size(this); if this.ptr_is_null(mask)? { let einval = this.eval_libc("EFAULT"); @@ -643,7 +640,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { _ => throw_unsup_format!("`sched_setaffinity` is only supported with a pid of 0 (indicating the current thread)"), }; - #[allow(clippy::map_entry)] if this.ptr_is_null(mask)? { let einval = this.eval_libc("EFAULT"); this.set_last_error(einval)?; @@ -652,9 +648,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // NOTE: cpusetsize might be smaller than `CpuAffinityMask::CPU_MASK_BYTES` let bits_slice = this.read_bytes_ptr_strip_provenance(mask, Size::from_bytes(cpusetsize))?; // This ignores the bytes beyond `CpuAffinityMask::CPU_MASK_BYTES` - let bits_array: [u8;CpuAffinityMask::CPU_MASK_BYTES] = + let bits_array: [u8; CpuAffinityMask::CPU_MASK_BYTES] = std::array::from_fn(|i| bits_slice.get(i).copied().unwrap_or(0)); - match CpuAffinityMask::from_array(&this.tcx.sess.target, this.machine.num_cpus, bits_array) { + match CpuAffinityMask::from_array(this, this.machine.num_cpus, bits_array) { Some(cpuset) => { this.machine.thread_cpu_affinity.insert(thread_id, cpuset); this.write_scalar(Scalar::from_i32(0), dest)?;