Skip to content

Commit 0805372

Browse files
committed
Auto merge of #2721 - a-b-c-1-2-3:fix-pagesize, r=RalfJung
Allow configurable and platform-specific page sizes This fixes #2644 by setting platform-default page sizes along with a command line flag to override size to a specific value (e.g. in the case of aarch64 Linux on M1 silicon). There's still some code cleanup to be done and tests need to be added but I'm opening this for now.
2 parents ac7a752 + 22628ff commit 0805372

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)