lookup c_ulong instead of hard-coding the chunk size

This commit is contained in:
Ralf Jung 2024-07-06 11:24:11 +02:00
parent c77a2c6c0c
commit 9a0e671cc2
5 changed files with 58 additions and 57 deletions

View file

@ -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::<core::ffi::c_ulong>()`
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<Self> {
// 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

View file

@ -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. \

View file

@ -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<Namespace>)
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<ty::Instance<'tcx>> {
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<ty::Instance<'tcx>> {
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).

View file

@ -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,

View file

@ -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)?;