Merge pull request #708 from RalfJung/alloc-tests
test System/Global allocator API: alloc_zeroed, realloc
This commit is contained in:
commit
8fd40db4bc
4 changed files with 150 additions and 69 deletions
178
src/fn_call.rs
178
src/fn_call.rs
|
|
@ -50,6 +50,86 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a + 'mir>: crate::MiriEvalContextExt<'
|
|||
Ok(Some(this.load_mir(instance.def)?))
|
||||
}
|
||||
|
||||
fn malloc(
|
||||
&mut self,
|
||||
size: u64,
|
||||
zero_init: bool,
|
||||
) -> Scalar<Tag> {
|
||||
let this = self.eval_context_mut();
|
||||
let tcx = &{this.tcx.tcx};
|
||||
if size == 0 {
|
||||
Scalar::from_int(0, this.pointer_size())
|
||||
} else {
|
||||
let align = this.tcx.data_layout.pointer_align.abi;
|
||||
let ptr = this.memory_mut().allocate(Size::from_bytes(size), align, MiriMemoryKind::C.into());
|
||||
if zero_init {
|
||||
// We just allocated this, the access cannot fail
|
||||
this.memory_mut()
|
||||
.get_mut(ptr.alloc_id).unwrap()
|
||||
.write_repeat(tcx, ptr, 0, Size::from_bytes(size)).unwrap();
|
||||
}
|
||||
Scalar::Ptr(ptr)
|
||||
}
|
||||
}
|
||||
|
||||
fn free(
|
||||
&mut self,
|
||||
ptr: Scalar<Tag>,
|
||||
) -> EvalResult<'tcx> {
|
||||
let this = self.eval_context_mut();
|
||||
if !ptr.is_null_ptr(this) {
|
||||
this.memory_mut().deallocate(
|
||||
ptr.to_ptr()?,
|
||||
None,
|
||||
MiriMemoryKind::C.into(),
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn realloc(
|
||||
&mut self,
|
||||
old_ptr: Scalar<Tag>,
|
||||
new_size: u64,
|
||||
) -> EvalResult<'tcx, Scalar<Tag>> {
|
||||
let this = self.eval_context_mut();
|
||||
let align = this.tcx.data_layout.pointer_align.abi;
|
||||
if old_ptr.is_null_ptr(this) {
|
||||
if new_size == 0 {
|
||||
Ok(Scalar::from_int(0, this.pointer_size()))
|
||||
} else {
|
||||
let new_ptr = this.memory_mut().allocate(
|
||||
Size::from_bytes(new_size),
|
||||
align,
|
||||
MiriMemoryKind::C.into()
|
||||
);
|
||||
Ok(Scalar::Ptr(new_ptr))
|
||||
}
|
||||
} else {
|
||||
let old_ptr = old_ptr.to_ptr()?;
|
||||
let memory = this.memory_mut();
|
||||
let old_size = Size::from_bytes(memory.get(old_ptr.alloc_id)?.bytes.len() as u64);
|
||||
if new_size == 0 {
|
||||
memory.deallocate(
|
||||
old_ptr,
|
||||
Some((old_size, align)),
|
||||
MiriMemoryKind::C.into(),
|
||||
)?;
|
||||
Ok(Scalar::from_int(0, this.pointer_size()))
|
||||
} else {
|
||||
let new_ptr = memory.reallocate(
|
||||
old_ptr,
|
||||
old_size,
|
||||
align,
|
||||
Size::from_bytes(new_size),
|
||||
align,
|
||||
MiriMemoryKind::C.into(),
|
||||
)?;
|
||||
Ok(Scalar::Ptr(new_ptr))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Emulates calling a foreign item, failing if the item is not supported.
|
||||
/// This function will handle `goto_block` if needed.
|
||||
fn emulate_foreign_item(
|
||||
|
|
@ -87,28 +167,15 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a + 'mir>: crate::MiriEvalContextExt<'
|
|||
match link_name {
|
||||
"malloc" => {
|
||||
let size = this.read_scalar(args[0])?.to_usize(this)?;
|
||||
if size == 0 {
|
||||
this.write_null(dest)?;
|
||||
} else {
|
||||
let align = this.tcx.data_layout.pointer_align.abi;
|
||||
let ptr = this.memory_mut().allocate(Size::from_bytes(size), align, MiriMemoryKind::C.into());
|
||||
this.write_scalar(Scalar::Ptr(ptr), dest)?;
|
||||
}
|
||||
let res = this.malloc(size, /*zero_init:*/ false);
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
"calloc" => {
|
||||
let items = this.read_scalar(args[0])?.to_usize(this)?;
|
||||
let len = this.read_scalar(args[1])?.to_usize(this)?;
|
||||
let bytes = items.checked_mul(len).ok_or_else(|| InterpError::Overflow(mir::BinOp::Mul))?;
|
||||
|
||||
if bytes == 0 {
|
||||
this.write_null(dest)?;
|
||||
} else {
|
||||
let size = Size::from_bytes(bytes);
|
||||
let align = this.tcx.data_layout.pointer_align.abi;
|
||||
let ptr = this.memory_mut().allocate(size, align, MiriMemoryKind::C.into());
|
||||
this.memory_mut().get_mut(ptr.alloc_id)?.write_repeat(tcx, ptr, 0, size)?;
|
||||
this.write_scalar(Scalar::Ptr(ptr), dest)?;
|
||||
}
|
||||
let size = items.checked_mul(len).ok_or_else(|| InterpError::Overflow(mir::BinOp::Mul))?;
|
||||
let res = this.malloc(size, /*zero_init:*/ true);
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
"posix_memalign" => {
|
||||
let ret = this.deref_operand(args[0])?;
|
||||
|
|
@ -136,55 +203,15 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a + 'mir>: crate::MiriEvalContextExt<'
|
|||
}
|
||||
this.write_null(dest)?;
|
||||
}
|
||||
|
||||
"free" => {
|
||||
let ptr = this.read_scalar(args[0])?.not_undef()?;
|
||||
if !ptr.is_null_ptr(this) {
|
||||
this.memory_mut().deallocate(
|
||||
ptr.to_ptr()?,
|
||||
None,
|
||||
MiriMemoryKind::C.into(),
|
||||
)?;
|
||||
}
|
||||
this.free(ptr)?;
|
||||
}
|
||||
"realloc" => {
|
||||
let old_ptr = this.read_scalar(args[0])?.not_undef()?;
|
||||
let new_size = this.read_scalar(args[1])?.to_usize(this)?;
|
||||
let align = this.tcx.data_layout.pointer_align.abi;
|
||||
if old_ptr.is_null_ptr(this) {
|
||||
if new_size == 0 {
|
||||
this.write_null(dest)?;
|
||||
} else {
|
||||
let new_ptr = this.memory_mut().allocate(
|
||||
Size::from_bytes(new_size),
|
||||
align,
|
||||
MiriMemoryKind::C.into()
|
||||
);
|
||||
this.write_scalar(Scalar::Ptr(new_ptr), dest)?;
|
||||
}
|
||||
} else {
|
||||
let old_ptr = old_ptr.to_ptr()?;
|
||||
let memory = this.memory_mut();
|
||||
let old_size = Size::from_bytes(memory.get(old_ptr.alloc_id)?.bytes.len() as u64);
|
||||
if new_size == 0 {
|
||||
memory.deallocate(
|
||||
old_ptr,
|
||||
Some((old_size, align)),
|
||||
MiriMemoryKind::C.into(),
|
||||
)?;
|
||||
this.write_null(dest)?;
|
||||
} else {
|
||||
let new_ptr = memory.reallocate(
|
||||
old_ptr,
|
||||
old_size,
|
||||
align,
|
||||
Size::from_bytes(new_size),
|
||||
align,
|
||||
MiriMemoryKind::C.into(),
|
||||
)?;
|
||||
this.write_scalar(Scalar::Ptr(new_ptr), dest)?;
|
||||
}
|
||||
}
|
||||
let res = this.realloc(old_ptr, new_size)?;
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
|
||||
"__rust_alloc" => {
|
||||
|
|
@ -687,6 +714,35 @@ pub trait EvalContextExt<'a, 'mir, 'tcx: 'a + 'mir>: crate::MiriEvalContextExt<'
|
|||
},
|
||||
|
||||
// Windows API stubs.
|
||||
// HANDLE = isize
|
||||
// DWORD = ULONG = u32
|
||||
"GetProcessHeap" => {
|
||||
// Just fake a HANDLE
|
||||
this.write_scalar(Scalar::from_int(1, this.pointer_size()), dest)?;
|
||||
}
|
||||
"HeapAlloc" => {
|
||||
let _handle = this.read_scalar(args[0])?.to_isize(this)?;
|
||||
let flags = this.read_scalar(args[1])?.to_u32()?;
|
||||
let size = this.read_scalar(args[2])?.to_usize(this)?;
|
||||
let zero_init = (flags & 0x00000008) != 0; // HEAP_ZERO_MEMORY
|
||||
let res = this.malloc(size, zero_init);
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
"HeapFree" => {
|
||||
let _handle = this.read_scalar(args[0])?.to_isize(this)?;
|
||||
let _flags = this.read_scalar(args[1])?.to_u32()?;
|
||||
let ptr = this.read_scalar(args[2])?.not_undef()?;
|
||||
this.free(ptr)?;
|
||||
}
|
||||
"HeapReAlloc" => {
|
||||
let _handle = this.read_scalar(args[0])?.to_isize(this)?;
|
||||
let _flags = this.read_scalar(args[1])?.to_u32()?;
|
||||
let ptr = this.read_scalar(args[2])?.not_undef()?;
|
||||
let size = this.read_scalar(args[3])?.to_usize(this)?;
|
||||
let res = this.realloc(ptr, size)?;
|
||||
this.write_scalar(res, dest)?;
|
||||
}
|
||||
|
||||
"SetLastError" => {
|
||||
let err = this.read_scalar(args[0])?.to_u32()?;
|
||||
this.machine.last_error = err;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
//ignore-windows: env var emulation not implemented on Windows
|
||||
//ignore-windows: TODO env var emulation stubbed out on Windows
|
||||
|
||||
use std::env;
|
||||
|
||||
|
|
|
|||
|
|
@ -27,13 +27,12 @@ fn test_map<S: BuildHasher>(mut map: HashMap<i32, i32, S>) {
|
|||
}
|
||||
|
||||
fn main() {
|
||||
if cfg!(not(target_os = "macos")) {
|
||||
let map: HashMap<i32, i32> = HashMap::default();
|
||||
test_map(map);
|
||||
} else {
|
||||
// TODO: Implement random number generation on OS X.
|
||||
if cfg!(target_os = "macos") { // TODO: Implement random number generation on OS X.
|
||||
// Until then, use a deterministic map.
|
||||
let map : HashMap<i32, i32, BuildHasherDefault<collections::hash_map::DefaultHasher>> = HashMap::default();
|
||||
test_map(map);
|
||||
} else {
|
||||
let map: HashMap<i32, i32> = HashMap::default();
|
||||
test_map(map);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,32 @@
|
|||
//ignore-windows: inspects allocation base address on Windows
|
||||
|
||||
#![feature(allocator_api)]
|
||||
|
||||
use std::ptr::NonNull;
|
||||
use std::alloc::{Global, Alloc, Layout, System};
|
||||
use std::slice;
|
||||
|
||||
fn check_alloc<T: Alloc>(mut allocator: T) { unsafe {
|
||||
let layout = Layout::from_size_align(20, 4).unwrap();
|
||||
let a = allocator.alloc(layout).unwrap();
|
||||
allocator.dealloc(a, layout);
|
||||
|
||||
let p1 = allocator.alloc_zeroed(layout).unwrap();
|
||||
|
||||
let p2 = allocator.realloc(p1, Layout::from_size_align(20, 4).unwrap(), 40).unwrap();
|
||||
let slice = slice::from_raw_parts(p2.as_ptr(), 20);
|
||||
assert_eq!(&slice, &[0_u8; 20]);
|
||||
|
||||
// old size == new size
|
||||
let p3 = allocator.realloc(p2, Layout::from_size_align(40, 4).unwrap(), 40).unwrap();
|
||||
let slice = slice::from_raw_parts(p3.as_ptr(), 20);
|
||||
assert_eq!(&slice, &[0_u8; 20]);
|
||||
|
||||
// old size > new size
|
||||
let p4 = allocator.realloc(p3, Layout::from_size_align(40, 4).unwrap(), 10).unwrap();
|
||||
let slice = slice::from_raw_parts(p4.as_ptr(), 10);
|
||||
assert_eq!(&slice, &[0_u8; 10]);
|
||||
|
||||
allocator.dealloc(p4, Layout::from_size_align(10, 4).unwrap());
|
||||
} }
|
||||
|
||||
fn check_overalign_requests<T: Alloc>(mut allocator: T) {
|
||||
let size = 8;
|
||||
|
|
@ -50,6 +73,9 @@ fn box_to_global() {
|
|||
}
|
||||
|
||||
fn main() {
|
||||
check_alloc(System);
|
||||
check_alloc(Global);
|
||||
#[cfg(not(target_os = "windows"))] // TODO: Inspects allocation base address on Windows; needs intptrcast model
|
||||
check_overalign_requests(System);
|
||||
check_overalign_requests(Global);
|
||||
global_to_box();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue