Skip to content

Commit 22628ff

Browse files
committed
add support for variable page sizes to miri
1 parent ac7a752 commit 22628ff

File tree

11 files changed

+78
-16
lines changed

11 files changed

+78
-16
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,8 @@ to Miri failing to detect cases of undefined behavior in a program.
399399
* `-Zmiri-track-weak-memory-loads` shows a backtrace when weak memory emulation returns an outdated
400400
value from a load. This can help diagnose problems that disappear under
401401
`-Zmiri-disable-weak-memory-emulation`.
402+
* `-Zmiri-force-page-size=<num>` overrides the default page size for an architecture, in multiples of 1k.
403+
`4` is default for most targets. This value should always be a power of 2 and nonzero.
402404

403405
[function ABI]: https://doc.rust-lang.org/reference/items/functions.html#extern-function-qualifier
404406

src/bin/miri.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,18 @@ fn main() {
512512
};
513513

514514
miri_config.num_cpus = num_cpus;
515+
} else if let Some(param) = arg.strip_prefix("-Zmiri-force-page-size=") {
516+
let page_size = match param.parse::<u64>() {
517+
Ok(i) =>
518+
if i.is_power_of_two() {
519+
i * 1024
520+
} else {
521+
show_error!("-Zmiri-force-page-size requires a power of 2: {}", i)
522+
},
523+
Err(err) => show_error!("-Zmiri-force-page-size requires a `u64`: {}", err),
524+
};
525+
526+
miri_config.page_size = Some(page_size);
515527
} else {
516528
// Forward to rustc.
517529
rustc_args.push(arg);

src/eval.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@ pub struct MiriConfig {
143143
pub gc_interval: u32,
144144
/// The number of CPUs to be reported by miri.
145145
pub num_cpus: u32,
146+
/// Requires Miri to emulate pages of a certain size
147+
pub page_size: Option<u64>,
146148
}
147149

148150
impl Default for MiriConfig {
@@ -176,6 +178,7 @@ impl Default for MiriConfig {
176178
external_so_file: None,
177179
gc_interval: 10_000,
178180
num_cpus: 1,
181+
page_size: None,
179182
}
180183
}
181184
}

src/intptrcast.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,12 @@ impl VisitTags for GlobalStateInner {
5151
}
5252

5353
impl GlobalStateInner {
54-
pub fn new(config: &MiriConfig) -> Self {
54+
pub fn new(config: &MiriConfig, stack_addr: u64) -> Self {
5555
GlobalStateInner {
5656
int_to_ptr_map: Vec::default(),
5757
base_addr: FxHashMap::default(),
5858
exposed: FxHashSet::default(),
59-
next_base_addr: STACK_ADDR,
59+
next_base_addr: stack_addr,
6060
provenance_mode: config.provenance_mode,
6161
}
6262
}

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ pub use crate::helpers::EvalContextExt as _;
107107
pub use crate::intptrcast::ProvenanceMode;
108108
pub use crate::machine::{
109109
AllocExtra, FrameExtra, MiriInterpCx, MiriInterpCxExt, MiriMachine, MiriMemoryKind,
110-
PrimitiveLayouts, Provenance, ProvenanceExtra, PAGE_SIZE, STACK_ADDR, STACK_SIZE,
110+
PrimitiveLayouts, Provenance, ProvenanceExtra,
111111
};
112112
pub use crate::mono_hash_map::MonoHashMap;
113113
pub use crate::operator::EvalContextExt as _;

src/machine.rs

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,6 @@ use crate::{
3131
*,
3232
};
3333

34-
// Some global facts about the emulated machine.
35-
pub const PAGE_SIZE: u64 = 4 * 1024; // FIXME: adjust to target architecture
36-
pub const STACK_ADDR: u64 = 32 * PAGE_SIZE; // not really about the "stack", but where we start assigning integer addresses to allocations
37-
pub const STACK_SIZE: u64 = 16 * PAGE_SIZE; // whatever
38-
3934
/// Extra data stored with each stack frame
4035
pub struct FrameExtra<'tcx> {
4136
/// Extra data for Stacked Borrows.
@@ -469,6 +464,10 @@ pub struct MiriMachine<'mir, 'tcx> {
469464
pub(crate) since_gc: u32,
470465
/// The number of CPUs to be reported by miri.
471466
pub(crate) num_cpus: u32,
467+
/// Determines Miri's page size and associated values
468+
pub(crate) page_size: u64,
469+
pub(crate) stack_addr: u64,
470+
pub(crate) stack_size: u64,
472471
}
473472

474473
impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
@@ -482,11 +481,31 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
482481
let rng = StdRng::seed_from_u64(config.seed.unwrap_or(0));
483482
let borrow_tracker = config.borrow_tracker.map(|bt| bt.instanciate_global_state(config));
484483
let data_race = config.data_race_detector.then(|| data_race::GlobalState::new(config));
484+
let page_size = if let Some(page_size) = config.page_size {
485+
page_size
486+
} else {
487+
let target = &layout_cx.tcx.sess.target;
488+
match target.arch.as_ref() {
489+
"wasm32" | "wasm64" => 64 * 1024, // https://webassembly.github.io/spec/core/exec/runtime.html#memory-instances
490+
"aarch64" =>
491+
if target.options.vendor.as_ref() == "apple" {
492+
// No "definitive" source, but see:
493+
// https://www.wwdcnotes.com/notes/wwdc20/10214/
494+
// https://github.com/ziglang/zig/issues/11308 etc.
495+
16 * 1024
496+
} else {
497+
4 * 1024
498+
},
499+
_ => 4 * 1024,
500+
}
501+
};
502+
let stack_addr = page_size * 32;
503+
let stack_size = page_size * 16;
485504
MiriMachine {
486505
tcx: layout_cx.tcx,
487506
borrow_tracker,
488507
data_race,
489-
intptrcast: RefCell::new(intptrcast::GlobalStateInner::new(config)),
508+
intptrcast: RefCell::new(intptrcast::GlobalStateInner::new(config, stack_addr)),
490509
// `env_vars` depends on a full interpreter so we cannot properly initialize it yet.
491510
env_vars: EnvVars::default(),
492511
main_fn_ret_place: None,
@@ -548,6 +567,9 @@ impl<'mir, 'tcx> MiriMachine<'mir, 'tcx> {
548567
gc_interval: config.gc_interval,
549568
since_gc: 0,
550569
num_cpus: config.num_cpus,
570+
page_size,
571+
stack_addr,
572+
stack_size,
551573
}
552574
}
553575

@@ -692,6 +714,9 @@ impl VisitTags for MiriMachine<'_, '_> {
692714
gc_interval: _,
693715
since_gc: _,
694716
num_cpus: _,
717+
page_size: _,
718+
stack_addr: _,
719+
stack_size: _,
695720
} = self;
696721

697722
threads.visit_tags(visit);

src/shims/unix/foreign_items.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
234234
// FIXME: Which of these are POSIX, and which are GNU/Linux?
235235
// At least the names seem to all also exist on macOS.
236236
let sysconfs: &[(&str, fn(&MiriInterpCx<'_, '_>) -> Scalar<Provenance>)] = &[
237-
("_SC_PAGESIZE", |this| Scalar::from_int(PAGE_SIZE, this.pointer_size())),
237+
("_SC_PAGESIZE", |this| Scalar::from_int(this.machine.page_size, this.pointer_size())),
238238
("_SC_NPROCESSORS_CONF", |this| Scalar::from_int(this.machine.num_cpus, this.pointer_size())),
239239
("_SC_NPROCESSORS_ONLN", |this| Scalar::from_int(this.machine.num_cpus, this.pointer_size())),
240240
// 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> {
496496
let [_attr, guard_size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
497497
let guard_size = this.deref_operand(guard_size)?;
498498
let guard_size_layout = this.libc_ty_layout("size_t")?;
499-
this.write_scalar(Scalar::from_uint(crate::PAGE_SIZE, guard_size_layout.size), &guard_size.into())?;
499+
this.write_scalar(Scalar::from_uint(this.machine.page_size, guard_size_layout.size), &guard_size.into())?;
500500

501501
// Return success (`0`).
502502
this.write_null(dest)?;
@@ -525,11 +525,11 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
525525
let size_place = this.deref_operand(size_place)?;
526526

527527
this.write_scalar(
528-
Scalar::from_uint(STACK_ADDR, this.pointer_size()),
528+
Scalar::from_uint(this.machine.stack_addr, this.pointer_size()),
529529
&addr_place.into(),
530530
)?;
531531
this.write_scalar(
532-
Scalar::from_uint(STACK_SIZE, this.pointer_size()),
532+
Scalar::from_uint(this.machine.stack_size, this.pointer_size()),
533533
&size_place.into(),
534534
)?;
535535

src/shims/unix/macos/foreign_items.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,13 +162,13 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
162162
"pthread_get_stackaddr_np" => {
163163
let [thread] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
164164
this.read_scalar(thread)?.to_machine_usize(this)?;
165-
let stack_addr = Scalar::from_uint(STACK_ADDR, this.pointer_size());
165+
let stack_addr = Scalar::from_uint(this.machine.stack_addr, this.pointer_size());
166166
this.write_scalar(stack_addr, dest)?;
167167
}
168168
"pthread_get_stacksize_np" => {
169169
let [thread] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
170170
this.read_scalar(thread)?.to_machine_usize(this)?;
171-
let stack_size = Scalar::from_uint(STACK_SIZE, this.pointer_size());
171+
let stack_size = Scalar::from_uint(this.machine.stack_size, this.pointer_size());
172172
this.write_scalar(stack_size, dest)?;
173173
}
174174

src/shims/windows/foreign_items.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
158158
// Set page size.
159159
let page_size = system_info.offset(field_offsets[2], dword_layout, &this.tcx)?;
160160
this.write_scalar(
161-
Scalar::from_int(PAGE_SIZE, dword_layout.size),
161+
Scalar::from_int(this.machine.page_size, dword_layout.size),
162162
&page_size.into(),
163163
)?;
164164
// Set number of processors.

tests/pass-dep/page_size.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,17 @@ fn main() {
33

44
// In particular, this checks that it is not 0.
55
assert!(page_size.is_power_of_two(), "page size not a power of two: {}", page_size);
6+
// Most architectures have 4k pages by default
7+
#[cfg(not(any(
8+
target_arch = "wasm32",
9+
target_arch = "wasm64",
10+
all(target_arch = "aarch64", target_vendor = "apple")
11+
)))]
12+
assert!(page_size == 4 * 1024, "non-4k default page size: {}", page_size);
13+
// ... except aarch64-apple with 16k
14+
#[cfg(all(target_arch = "aarch64", target_vendor = "apple"))]
15+
assert!(page_size == 16 * 1024, "aarch64 apple reports non-16k page size: {}", page_size);
16+
// ... and wasm with 64k
17+
#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
18+
assert!(page_size == 64 * 1024, "wasm reports non-64k page size: {}", page_size);
619
}

tests/pass-dep/page_size_override.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
//@compile-flags: -Zmiri-force-page-size=8
2+
3+
fn main() {
4+
let page_size = page_size::get();
5+
6+
assert!(page_size == 8 * 1024, "8k page size override not respected: {}", page_size);
7+
}

0 commit comments

Comments
 (0)