1
Fork 0

Merge pull request #44 from oli-obk/alignment

Alignment
This commit is contained in:
Scott Olson 2016-07-25 18:24:15 -05:00 committed by GitHub
commit 90c2470e22
6 changed files with 143 additions and 57 deletions

View file

@ -36,6 +36,10 @@ pub enum EvalError<'tcx> {
},
ExecutionTimeLimitReached,
StackFrameLimitReached,
AlignmentCheckFailed {
required: usize,
has: usize,
},
}
pub type EvalResult<'tcx, T> = Result<T, EvalError<'tcx>>;
@ -82,6 +86,8 @@ impl<'tcx> Error for EvalError<'tcx> {
"reached the configured maximum execution time",
EvalError::StackFrameLimitReached =>
"reached the configured maximum number of stack frames",
EvalError::AlignmentCheckFailed{..} =>
"tried to execute a misaligned read or write",
}
}
@ -106,6 +112,9 @@ impl<'tcx> fmt::Display for EvalError<'tcx> {
EvalError::OutOfMemory { allocation_size, memory_size, memory_usage } =>
write!(f, "tried to allocate {} more bytes, but only {} bytes are free of the {} byte memory",
allocation_size, memory_size - memory_usage, memory_size),
EvalError::AlignmentCheckFailed { required, has } =>
write!(f, "tried to access memory with alignment {}, but alignment {} is required",
has, required),
_ => write!(f, "{}", self.description()),
}
}

View file

@ -152,7 +152,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
match output_ty {
ty::FnConverging(ty) => {
let size = self.type_size_with_substs(ty, substs);
self.memory.allocate(size).map(Some)
let align = self.type_align_with_substs(ty, substs);
self.memory.allocate(size, align).map(Some)
}
ty::FnDiverging => Ok(None),
}
@ -176,19 +177,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
use rustc_const_math::{ConstInt, ConstIsize, ConstUsize, ConstFloat};
macro_rules! i2p {
($i:ident, $n:expr) => {{
let ptr = self.memory.allocate($n)?;
let ptr = self.memory.allocate($n, $n)?;
self.memory.write_int(ptr, $i as i64, $n)?;
Ok(ptr)
}}
}
match *const_val {
Float(ConstFloat::F32(f)) => {
let ptr = self.memory.allocate(4)?;
let ptr = self.memory.allocate(4, 4)?;
self.memory.write_f32(ptr, f)?;
Ok(ptr)
},
Float(ConstFloat::F64(f)) => {
let ptr = self.memory.allocate(8)?;
let ptr = self.memory.allocate(8, 8)?;
self.memory.write_f64(ptr, f)?;
Ok(ptr)
},
@ -197,22 +198,22 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
Integral(ConstInt::InferSigned(_)) => unreachable!(),
Integral(ConstInt::I8(i)) => i2p!(i, 1),
Integral(ConstInt::U8(i)) => i2p!(i, 1),
Integral(ConstInt::Isize(ConstIsize::Is16(i))) |
Integral(ConstInt::I16(i)) => i2p!(i, 2),
Integral(ConstInt::Usize(ConstUsize::Us16(i))) |
Integral(ConstInt::U16(i)) => i2p!(i, 2),
Integral(ConstInt::Isize(ConstIsize::Is32(i))) |
Integral(ConstInt::I32(i)) => i2p!(i, 4),
Integral(ConstInt::Usize(ConstUsize::Us32(i))) |
Integral(ConstInt::U32(i)) => i2p!(i, 4),
Integral(ConstInt::Isize(ConstIsize::Is64(i))) |
Integral(ConstInt::I64(i)) => i2p!(i, 8),
Integral(ConstInt::Usize(ConstUsize::Us64(i))) |
Integral(ConstInt::U64(i)) => i2p!(i, 8),
Integral(ConstInt::Isize(ConstIsize::Is16(i))) => i2p!(i, 2),
Integral(ConstInt::Isize(ConstIsize::Is32(i))) => i2p!(i, 4),
Integral(ConstInt::Isize(ConstIsize::Is64(i))) => i2p!(i, 8),
Integral(ConstInt::Usize(ConstUsize::Us16(i))) => i2p!(i, 2),
Integral(ConstInt::Usize(ConstUsize::Us32(i))) => i2p!(i, 4),
Integral(ConstInt::Usize(ConstUsize::Us64(i))) => i2p!(i, 8),
Str(ref s) => {
let psize = self.memory.pointer_size();
let static_ptr = self.memory.allocate(s.len())?;
let ptr = self.memory.allocate(psize * 2)?;
let static_ptr = self.memory.allocate(s.len(), 1)?;
let ptr = self.memory.allocate(psize * 2, psize)?;
self.memory.write_bytes(static_ptr, s.as_bytes())?;
self.memory.write_ptr(ptr, static_ptr)?;
self.memory.write_usize(ptr.offset(psize as isize), s.len() as u64)?;
@ -220,19 +221,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
ByteStr(ref bs) => {
let psize = self.memory.pointer_size();
let static_ptr = self.memory.allocate(bs.len())?;
let ptr = self.memory.allocate(psize)?;
let static_ptr = self.memory.allocate(bs.len(), 1)?;
let ptr = self.memory.allocate(psize, psize)?;
self.memory.write_bytes(static_ptr, bs)?;
self.memory.write_ptr(ptr, static_ptr)?;
Ok(ptr)
}
Bool(b) => {
let ptr = self.memory.allocate(1)?;
let ptr = self.memory.allocate(1, 1)?;
self.memory.write_bool(ptr, b)?;
Ok(ptr)
}
Char(c) => {
let ptr = self.memory.allocate(4)?;
let ptr = self.memory.allocate(4, 4)?;
self.memory.write_uint(ptr, c as u64, 4)?;
Ok(ptr)
},
@ -278,10 +279,18 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
self.type_size_with_substs(ty, self.substs())
}
fn type_align(&self, ty: Ty<'tcx>) -> usize {
self.type_align_with_substs(ty, self.substs())
}
fn type_size_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> usize {
self.type_layout_with_substs(ty, substs).size(&self.tcx.data_layout).bytes() as usize
}
fn type_align_with_substs(&self, ty: Ty<'tcx>, substs: &'tcx Substs<'tcx>) -> usize {
self.type_layout_with_substs(ty, substs).align(&self.tcx.data_layout).abi() as usize
}
fn type_layout(&self, ty: Ty<'tcx>) -> &'tcx Layout {
self.type_layout_with_substs(ty, self.substs())
}
@ -315,7 +324,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let locals: EvalResult<'tcx, Vec<Pointer>> = arg_tys.chain(var_tys).chain(temp_tys).map(|ty| {
let size = self.type_size_with_substs(ty, substs);
self.memory.allocate(size)
let align = self.type_align_with_substs(ty, substs);
self.memory.allocate(size, align)
}).collect();
self.stack.push(Frame {
@ -519,15 +529,15 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
Repeat(ref operand, _) => {
let (elem_size, length) = match dest_ty.sty {
ty::TyArray(elem_ty, n) => (self.type_size(elem_ty), n),
let (elem_size, elem_align, length) = match dest_ty.sty {
ty::TyArray(elem_ty, n) => (self.type_size(elem_ty), self.type_align(elem_ty), n),
_ => panic!("tried to assign array-repeat to non-array type {:?}", dest_ty),
};
let src = self.eval_operand(operand)?;
for i in 0..length {
let elem_dest = dest.offset((i * elem_size) as isize);
self.memory.copy(src, elem_dest, elem_size)?;
self.memory.copy(src, elem_dest, elem_size, elem_align)?;
}
}
@ -562,7 +572,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
Box(ty) => {
let size = self.type_size(ty);
let ptr = self.memory.allocate(size)?;
let align = self.type_align(ty);
let ptr = self.memory.allocate(size, align)?;
self.memory.write_ptr(dest, ptr)?;
}
@ -593,13 +604,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
warn!("misc cast from {:?} to {:?}", src_ty, dest_ty);
let dest_size = self.type_size(dest_ty);
let src_size = self.type_size(src_ty);
let dest_align = self.type_align(dest_ty);
// Hack to support fat pointer -> thin pointer casts to keep tests for
// other things passing for now.
let is_fat_ptr_cast = pointee_type(src_ty).map_or(false, |ty| !self.type_is_sized(ty));
if dest_size == src_size || is_fat_ptr_cast {
self.memory.copy(src, dest, dest_size)?;
self.memory.copy(src, dest, dest_size, dest_align)?;
} else {
return Err(EvalError::Unimplemented(format!("can't handle cast: {:?}", rvalue)));
}
@ -710,7 +722,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
Item { def_id, substs } => {
if let ty::TyFnDef(..) = ty.sty {
// function items are zero sized
Ok(self.memory.allocate(0)?)
Ok(self.memory.allocate(0, 0)?)
} else {
let cid = ConstantId {
def_id: def_id,
@ -843,7 +855,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
fn move_(&mut self, src: Pointer, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, ()> {
let size = self.type_size(ty);
self.memory.copy(src, dest, size)?;
let align = self.type_align(ty);
self.memory.copy(src, dest, size, align)?;
Ok(())
}
@ -967,9 +980,9 @@ pub fn eval_main<'a, 'tcx: 'a>(
if mir.arg_decls.len() == 2 {
// start function
let ptr_size = ecx.memory().pointer_size();
let nargs = ecx.memory_mut().allocate(ptr_size).expect("can't allocate memory for nargs");
let nargs = ecx.memory_mut().allocate(ptr_size, ptr_size).expect("can't allocate memory for nargs");
ecx.memory_mut().write_usize(nargs, 0).unwrap();
let args = ecx.memory_mut().allocate(ptr_size).expect("can't allocate memory for arg pointer");
let args = ecx.memory_mut().allocate(ptr_size, ptr_size).expect("can't allocate memory for arg pointer");
ecx.memory_mut().write_usize(args, 0).unwrap();
ecx.frame_mut().locals[0] = nargs;
ecx.frame_mut().locals[1] = args;

View file

@ -88,7 +88,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
match func_ty.sty {
ty::TyFnPtr(bare_fn_ty) => {
let ptr = self.eval_operand(func)?;
assert_eq!(ptr.offset, 0);
let fn_ptr = self.memory.read_ptr(ptr)?;
let FunctionDefinition { def_id, substs, fn_ty } = self.memory.get_fn(fn_ptr.alloc_id)?;
if fn_ty != bare_fn_ty {
@ -290,10 +289,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
"copy_nonoverlapping" => {
let elem_ty = *substs.types.get(subst::FnSpace, 0);
let elem_size = self.type_size(elem_ty);
let elem_align = self.type_align(elem_ty);
let src = self.memory.read_ptr(args_ptrs[0])?;
let dest = self.memory.read_ptr(args_ptrs[1])?;
let count = self.memory.read_isize(args_ptrs[2])?;
self.memory.copy(src, dest, count as usize * elem_size)?;
self.memory.copy(src, dest, count as usize * elem_size, elem_align)?;
}
"discriminant_value" => {
@ -308,8 +308,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
"init" => self.memory.write_repeat(dest, 0, dest_layout.size(&self.tcx.data_layout).bytes() as usize)?,
"min_align_of" => {
// FIXME: use correct value
self.memory.write_int(dest, 1, pointer_size)?;
let elem_ty = *substs.types.get(subst::FnSpace, 0);
let elem_align = self.type_align(elem_ty);
self.memory.write_uint(dest, elem_align as u64, pointer_size)?;
}
"move_val_init" => {
@ -416,14 +417,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
match &link_name[..] {
"__rust_allocate" => {
let size = self.memory.read_usize(args[0])?;
let ptr = self.memory.allocate(size as usize)?;
let align = self.memory.read_usize(args[1])?;
let ptr = self.memory.allocate(size as usize, align as usize)?;
self.memory.write_ptr(dest, ptr)?;
}
"__rust_reallocate" => {
let ptr = self.memory.read_ptr(args[0])?;
let size = self.memory.read_usize(args[2])?;
let new_ptr = self.memory.reallocate(ptr, size as usize)?;
let align = self.memory.read_usize(args[3])?;
let new_ptr = self.memory.reallocate(ptr, size as usize, align as usize)?;
self.memory.write_ptr(dest, new_ptr)?;
}

View file

@ -29,6 +29,7 @@ pub struct Allocation {
pub bytes: Vec<u8>,
pub relocations: BTreeMap<usize, AllocId>,
pub undef_mask: UndefMask,
pub align: usize,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
@ -98,15 +99,16 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
bytes: Vec::new(),
relocations: BTreeMap::new(),
undef_mask: UndefMask::new(0),
align: 1,
};
mem.alloc_map.insert(ZST_ALLOC_ID, alloc);
// check that additional zst allocs work
debug_assert!(mem.allocate(0).unwrap().points_to_zst());
debug_assert!(mem.allocate(0, 1).unwrap().points_to_zst());
debug_assert!(mem.get(ZST_ALLOC_ID).is_ok());
mem
}
pub fn allocations<'b>(&'b self) -> ::std::collections::hash_map::Iter<'b, AllocId, Allocation> {
pub fn allocations(&self) -> ::std::collections::hash_map::Iter<AllocId, Allocation> {
self.alloc_map.iter()
}
@ -133,7 +135,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
}
}
pub fn allocate(&mut self, size: usize) -> EvalResult<'tcx, Pointer> {
pub fn allocate(&mut self, size: usize, align: usize) -> EvalResult<'tcx, Pointer> {
assert!(align != 0);
if size == 0 {
return Ok(Pointer::zst_ptr());
}
@ -149,6 +152,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
bytes: vec![0; size],
relocations: BTreeMap::new(),
undef_mask: UndefMask::new(size),
align: align,
};
let id = self.next_id;
self.next_id.0 += 1;
@ -161,16 +165,16 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
// TODO(solson): Track which allocations were returned from __rust_allocate and report an error
// when reallocating/deallocating any others.
pub fn reallocate(&mut self, ptr: Pointer, new_size: usize) -> EvalResult<'tcx, Pointer> {
pub fn reallocate(&mut self, ptr: Pointer, new_size: usize, align: usize) -> EvalResult<'tcx, Pointer> {
// TODO(solson): Report error about non-__rust_allocate'd pointer.
if ptr.offset != 0 {
// TODO(solson): Report error about non-__rust_allocate'd pointer.
return Err(EvalError::Unimplemented(format!("bad pointer offset: {}", ptr.offset)));
}
if ptr.points_to_zst() {
return self.allocate(new_size);
return self.allocate(new_size, align);
}
let size = self.get_mut(ptr.alloc_id)?.bytes.len();
let size = self.get(ptr.alloc_id)?.bytes.len();
if new_size > size {
let amount = new_size - size;
@ -187,7 +191,10 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
alloc.undef_mask.truncate(new_size);
}
Ok(ptr)
Ok(Pointer {
alloc_id: ptr.alloc_id,
offset: 0,
})
}
// TODO(solson): See comment on `reallocate`.
@ -220,6 +227,24 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
pub fn endianess(&self) -> layout::Endian {
self.layout.endian
}
pub fn check_align(&self, ptr: Pointer, align: usize) -> EvalResult<'tcx, ()> {
let alloc = self.get(ptr.alloc_id)?;
if alloc.align < align {
return Err(EvalError::AlignmentCheckFailed {
has: alloc.align,
required: align,
});
}
if ptr.offset % align == 0 {
Ok(())
} else {
Err(EvalError::AlignmentCheckFailed {
has: ptr.offset % align,
required: align,
})
}
}
}
/// Allocation accessors
@ -337,7 +362,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
Ok(&mut alloc.bytes[ptr.offset..ptr.offset + size])
}
fn get_bytes(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, &[u8]> {
fn get_bytes(&self, ptr: Pointer, size: usize, align: usize) -> EvalResult<'tcx, &[u8]> {
self.check_align(ptr, align)?;
if self.relocations(ptr, size)?.count() != 0 {
return Err(EvalError::ReadPointerAsBytes);
}
@ -345,7 +371,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
self.get_bytes_unchecked(ptr, size)
}
fn get_bytes_mut(&mut self, ptr: Pointer, size: usize) -> EvalResult<'tcx, &mut [u8]> {
fn get_bytes_mut(&mut self, ptr: Pointer, size: usize, align: usize) -> EvalResult<'tcx, &mut [u8]> {
self.check_align(ptr, align)?;
self.clear_relocations(ptr, size)?;
self.mark_definedness(ptr, size, true)?;
self.get_bytes_unchecked_mut(ptr, size)
@ -354,11 +381,11 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
/// Reading and writing
impl<'a, 'tcx> Memory<'a, 'tcx> {
pub fn copy(&mut self, src: Pointer, dest: Pointer, size: usize) -> EvalResult<'tcx, ()> {
pub fn copy(&mut self, src: Pointer, dest: Pointer, size: usize, align: usize) -> EvalResult<'tcx, ()> {
self.check_relocation_edges(src, size)?;
let src_bytes = self.get_bytes_unchecked_mut(src, size)?.as_mut_ptr();
let dest_bytes = self.get_bytes_mut(dest, size)?.as_mut_ptr();
let dest_bytes = self.get_bytes_mut(dest, size, align)?.as_mut_ptr();
// SAFE: The above indexing would have panicked if there weren't at least `size` bytes
// behind `src` and `dest`. Also, we use the overlapping-safe `ptr::copy` if `src` and
@ -378,17 +405,17 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
}
pub fn read_bytes(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, &[u8]> {
self.get_bytes(ptr, size)
self.get_bytes(ptr, size, 1)
}
pub fn write_bytes(&mut self, ptr: Pointer, src: &[u8]) -> EvalResult<'tcx, ()> {
let bytes = self.get_bytes_mut(ptr, src.len())?;
let bytes = self.get_bytes_mut(ptr, src.len(), 1)?;
bytes.clone_from_slice(src);
Ok(())
}
pub fn write_repeat(&mut self, ptr: Pointer, val: u8, count: usize) -> EvalResult<'tcx, ()> {
let bytes = self.get_bytes_mut(ptr, count)?;
let bytes = self.get_bytes_mut(ptr, count, 1)?;
for b in bytes { *b = val; }
Ok(())
}
@ -434,7 +461,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
}
pub fn read_bool(&self, ptr: Pointer) -> EvalResult<'tcx, bool> {
let bytes = self.get_bytes(ptr, 1)?;
let bytes = self.get_bytes(ptr, 1, self.layout.i1_align.abi() as usize)?;
match bytes[0] {
0 => Ok(false),
1 => Ok(true),
@ -443,27 +470,43 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
}
pub fn write_bool(&mut self, ptr: Pointer, b: bool) -> EvalResult<'tcx, ()> {
self.get_bytes_mut(ptr, 1).map(|bytes| bytes[0] = b as u8)
let align = self.layout.i1_align.abi() as usize;
self.get_bytes_mut(ptr, 1, align)
.map(|bytes| bytes[0] = b as u8)
}
fn int_align(&self, size: usize) -> EvalResult<'tcx, usize> {
match size {
1 => Ok(self.layout.i8_align.abi() as usize),
2 => Ok(self.layout.i16_align.abi() as usize),
4 => Ok(self.layout.i32_align.abi() as usize),
8 => Ok(self.layout.i64_align.abi() as usize),
_ => panic!("bad integer size"),
}
}
pub fn read_int(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, i64> {
self.get_bytes(ptr, size).map(|b| read_target_int(self.endianess(), b).unwrap())
let align = self.int_align(size)?;
self.get_bytes(ptr, size, align).map(|b| read_target_int(self.endianess(), b).unwrap())
}
pub fn write_int(&mut self, ptr: Pointer, n: i64, size: usize) -> EvalResult<'tcx, ()> {
let align = self.int_align(size)?;
let endianess = self.endianess();
let b = self.get_bytes_mut(ptr, size)?;
let b = self.get_bytes_mut(ptr, size, align)?;
write_target_int(endianess, b, n).unwrap();
Ok(())
}
pub fn read_uint(&self, ptr: Pointer, size: usize) -> EvalResult<'tcx, u64> {
self.get_bytes(ptr, size).map(|b| read_target_uint(self.endianess(), b).unwrap())
let align = self.int_align(size)?;
self.get_bytes(ptr, size, align).map(|b| read_target_uint(self.endianess(), b).unwrap())
}
pub fn write_uint(&mut self, ptr: Pointer, n: u64, size: usize) -> EvalResult<'tcx, ()> {
let align = self.int_align(size)?;
let endianess = self.endianess();
let b = self.get_bytes_mut(ptr, size)?;
let b = self.get_bytes_mut(ptr, size, align)?;
write_target_uint(endianess, b, n).unwrap();
Ok(())
}
@ -488,24 +531,28 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
pub fn write_f32(&mut self, ptr: Pointer, f: f32) -> EvalResult<'tcx, ()> {
let endianess = self.endianess();
let b = self.get_bytes_mut(ptr, 4)?;
let align = self.layout.f32_align.abi() as usize;
let b = self.get_bytes_mut(ptr, 4, align)?;
write_target_f32(endianess, b, f).unwrap();
Ok(())
}
pub fn write_f64(&mut self, ptr: Pointer, f: f64) -> EvalResult<'tcx, ()> {
let endianess = self.endianess();
let b = self.get_bytes_mut(ptr, 8)?;
let align = self.layout.f64_align.abi() as usize;
let b = self.get_bytes_mut(ptr, 8, align)?;
write_target_f64(endianess, b, f).unwrap();
Ok(())
}
pub fn read_f32(&self, ptr: Pointer) -> EvalResult<'tcx, f32> {
self.get_bytes(ptr, 4).map(|b| read_target_f32(self.endianess(), b).unwrap())
self.get_bytes(ptr, 4, self.layout.f32_align.abi() as usize)
.map(|b| read_target_f32(self.endianess(), b).unwrap())
}
pub fn read_f64(&self, ptr: Pointer) -> EvalResult<'tcx, f64> {
self.get_bytes(ptr, 8).map(|b| read_target_f64(self.endianess(), b).unwrap())
self.get_bytes(ptr, 8, self.layout.f64_align.abi() as usize)
.map(|b| read_target_f64(self.endianess(), b).unwrap())
}
}

View file

@ -1,3 +1,6 @@
#![allow(unknown_lints)]
#![allow(float_cmp)]
use rustc::mir::repr as mir;
use error::{EvalError, EvalResult};

View file

@ -0,0 +1,11 @@
fn main() {
// miri always gives allocations the worst possible alignment, so a `u8` array is guaranteed
// to be at the virtual location 1 (so one byte offset from the ultimate alignemnt location 0)
let mut x = [0u8; 20];
let x_ptr: *mut u8 = &mut x[0];
let y_ptr = x_ptr as *mut u64;
unsafe {
*y_ptr = 42; //~ ERROR tried to access memory with alignment 1, but alignment
}
panic!("unreachable in miri");
}