add support for variable page sizes to miri

This commit is contained in:
Nia Espera 2022-12-09 17:59:06 +01:00
parent 6d4dd6e629
commit 7949f21cbe
11 changed files with 78 additions and 16 deletions

View file

@ -399,6 +399,8 @@ to Miri failing to detect cases of undefined behavior in a program.
* `-Zmiri-track-weak-memory-loads` shows a backtrace when weak memory emulation returns an outdated
value from a load. This can help diagnose problems that disappear under
`-Zmiri-disable-weak-memory-emulation`.
* `-Zmiri-force-page-size=<num>` overrides the default page size for an architecture, in multiples of 1k.
`4` is default for most targets. This value should always be a power of 2 and nonzero.
[function ABI]: https://doc.rust-lang.org/reference/items/functions.html#extern-function-qualifier

View file

@ -512,6 +512,18 @@ fn main() {
};
miri_config.num_cpus = num_cpus;
} else if let Some(param) = arg.strip_prefix("-Zmiri-force-page-size=") {
let page_size = match param.parse::<u64>() {
Ok(i) =>
if i.is_power_of_two() {
i * 1024
} else {
show_error!("-Zmiri-force-page-size requires a power of 2: {}", i)
},
Err(err) => show_error!("-Zmiri-force-page-size requires a `u64`: {}", err),
};
miri_config.page_size = Some(page_size);
} else {
// Forward to rustc.
rustc_args.push(arg);

View file

@ -143,6 +143,8 @@ pub struct MiriConfig {
pub gc_interval: u32,
/// The number of CPUs to be reported by miri.
pub num_cpus: u32,
/// Requires Miri to emulate pages of a certain size
pub page_size: Option<u64>,
}
impl Default for MiriConfig {
@ -176,6 +178,7 @@ impl Default for MiriConfig {
external_so_file: None,
gc_interval: 10_000,
num_cpus: 1,
page_size: None,
}
}
}

View file

@ -51,12 +51,12 @@ impl VisitTags for GlobalStateInner {
}
impl GlobalStateInner {
pub fn new(config: &MiriConfig) -> Self {
pub fn new(config: &MiriConfig, stack_addr: u64) -> Self {
GlobalStateInner {
int_to_ptr_map: Vec::default(),
base_addr: FxHashMap::default(),
exposed: FxHashSet::default(),
next_base_addr: STACK_ADDR,
next_base_addr: stack_addr,
provenance_mode: config.provenance_mode,
}
}

View file

@ -107,7 +107,7 @@ pub use crate::helpers::EvalContextExt as _;
pub use crate::intptrcast::ProvenanceMode;
pub use crate::machine::{
AllocExtra, FrameExtra, MiriInterpCx, MiriInterpCxExt, MiriMachine, MiriMemoryKind,
PrimitiveLayouts, Provenance, ProvenanceExtra, PAGE_SIZE, STACK_ADDR, STACK_SIZE,
PrimitiveLayouts, Provenance, ProvenanceExtra,
};
pub use crate::mono_hash_map::MonoHashMap;
pub use crate::operator::EvalContextExt as _;

View file

@ -31,11 +31,6 @@ use crate::{
*,
};
// Some global facts about the emulated machine.
pub const PAGE_SIZE: u64 = 4 * 1024; // FIXME: adjust to target architecture
pub const STACK_ADDR: u64 = 32 * PAGE_SIZE; // not really about the "stack", but where we start assigning integer addresses to allocations
pub const STACK_SIZE: u64 = 16 * PAGE_SIZE; // whatever
/// Extra data stored with each stack frame
pub struct FrameExtra<'tcx> {
/// Extra data for Stacked Borrows.
@ -469,6 +464,10 @@ pub struct MiriMachine<'mir, 'tcx> {
pub(crate) since_gc: u32,
/// The number of CPUs to be reported by miri.
pub(crate) num_cpus: u32,
/// Determines Miri's page size and associated values
pub(crate) page_size: u64,
pub(crate) stack_addr: u64,
pub(crate) stack_size: u64,
}
impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
@ -482,11 +481,31 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
let rng = StdRng::seed_from_u64(config.seed.unwrap_or(0));
let borrow_tracker = config.borrow_tracker.map(|bt| bt.instanciate_global_state(config));
let data_race = config.data_race_detector.then(|| data_race::GlobalState::new(config));
let page_size = if let Some(page_size) = config.page_size {
page_size
} else {
let target = &layout_cx.tcx.sess.target;
match target.arch.as_ref() {
"wasm32" | "wasm64" => 64 * 1024, // https://webassembly.github.io/spec/core/exec/runtime.html#memory-instances
"aarch64" =>
if target.options.vendor.as_ref() == "apple" {
// No "definitive" source, but see:
// https://www.wwdcnotes.com/notes/wwdc20/10214/
// https://github.com/ziglang/zig/issues/11308 etc.
16 * 1024
} else {
4 * 1024
},
_ => 4 * 1024,
}
};
let stack_addr = page_size * 32;
let stack_size = page_size * 16;
MiriMachine {
tcx: layout_cx.tcx,
borrow_tracker,
data_race,
intptrcast: RefCell::new(intptrcast::GlobalStateInner::new(config)),
intptrcast: RefCell::new(intptrcast::GlobalStateInner::new(config, stack_addr)),
// `env_vars` depends on a full interpreter so we cannot properly initialize it yet.
env_vars: EnvVars::default(),
main_fn_ret_place: None,
@ -548,6 +567,9 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
gc_interval: config.gc_interval,
since_gc: 0,
num_cpus: config.num_cpus,
page_size,
stack_addr,
stack_size,
}
}
@ -692,6 +714,9 @@ impl VisitTags for MiriMachine<'_, '_> {
gc_interval: _,
since_gc: _,
num_cpus: _,
page_size: _,
stack_addr: _,
stack_size: _,
} = self;
threads.visit_tags(visit);

View file

@ -234,7 +234,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
// FIXME: Which of these are POSIX, and which are GNU/Linux?
// At least the names seem to all also exist on macOS.
let sysconfs: &[(&str, fn(&MiriInterpCx<'_, '_>) -> Scalar<Provenance>)] = &[
("_SC_PAGESIZE", |this| Scalar::from_int(PAGE_SIZE, this.pointer_size())),
("_SC_PAGESIZE", |this| Scalar::from_int(this.machine.page_size, this.pointer_size())),
("_SC_NPROCESSORS_CONF", |this| Scalar::from_int(this.machine.num_cpus, this.pointer_size())),
("_SC_NPROCESSORS_ONLN", |this| Scalar::from_int(this.machine.num_cpus, this.pointer_size())),
// 512 seems to be a reasonable default. The value is not critical, in
@ -496,7 +496,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
let [_attr, guard_size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
let guard_size = this.deref_operand(guard_size)?;
let guard_size_layout = this.libc_ty_layout("size_t")?;
this.write_scalar(Scalar::from_uint(crate::PAGE_SIZE, guard_size_layout.size), &guard_size.into())?;
this.write_scalar(Scalar::from_uint(this.machine.page_size, guard_size_layout.size), &guard_size.into())?;
// Return success (`0`).
this.write_null(dest)?;
@ -525,11 +525,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
let size_place = this.deref_operand(size_place)?;
this.write_scalar(
Scalar::from_uint(STACK_ADDR, this.pointer_size()),
Scalar::from_uint(this.machine.stack_addr, this.pointer_size()),
&addr_place.into(),
)?;
this.write_scalar(
Scalar::from_uint(STACK_SIZE, this.pointer_size()),
Scalar::from_uint(this.machine.stack_size, this.pointer_size()),
&size_place.into(),
)?;

View file

@ -162,13 +162,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
"pthread_get_stackaddr_np" => {
let [thread] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
this.read_scalar(thread)?.to_machine_usize(this)?;
let stack_addr = Scalar::from_uint(STACK_ADDR, this.pointer_size());
let stack_addr = Scalar::from_uint(this.machine.stack_addr, this.pointer_size());
this.write_scalar(stack_addr, dest)?;
}
"pthread_get_stacksize_np" => {
let [thread] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
this.read_scalar(thread)?.to_machine_usize(this)?;
let stack_size = Scalar::from_uint(STACK_SIZE, this.pointer_size());
let stack_size = Scalar::from_uint(this.machine.stack_size, this.pointer_size());
this.write_scalar(stack_size, dest)?;
}

View file

@ -158,7 +158,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
// Set page size.
let page_size = system_info.offset(field_offsets[2], dword_layout, &this.tcx)?;
this.write_scalar(
Scalar::from_int(PAGE_SIZE, dword_layout.size),
Scalar::from_int(this.machine.page_size, dword_layout.size),
&page_size.into(),
)?;
// Set number of processors.

View file

@ -3,4 +3,17 @@ fn main() {
// In particular, this checks that it is not 0.
assert!(page_size.is_power_of_two(), "page size not a power of two: {}", page_size);
// Most architectures have 4k pages by default
#[cfg(not(any(
target_arch = "wasm32",
target_arch = "wasm64",
all(target_arch = "aarch64", target_vendor = "apple")
)))]
assert!(page_size == 4 * 1024, "non-4k default page size: {}", page_size);
// ... except aarch64-apple with 16k
#[cfg(all(target_arch = "aarch64", target_vendor = "apple"))]
assert!(page_size == 16 * 1024, "aarch64 apple reports non-16k page size: {}", page_size);
// ... and wasm with 64k
#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
assert!(page_size == 64 * 1024, "wasm reports non-64k page size: {}", page_size);
}

View file

@ -0,0 +1,7 @@
//@compile-flags: -Zmiri-force-page-size=8
fn main() {
let page_size = page_size::get();
assert!(page_size == 8 * 1024, "8k page size override not respected: {}", page_size);
}