Merge pull request #274 from RalfJung/packed2
make force_allocation handle packed ByValPair
This commit is contained in:
commit
7c6befe69f
9 changed files with 189 additions and 141 deletions
|
|
@ -142,6 +142,12 @@ impl Default for ResourceLimits {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct TyAndPacked<'tcx> {
|
||||
pub ty: Ty<'tcx>,
|
||||
pub packed: bool,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, limits: ResourceLimits) -> Self {
|
||||
EvalContext {
|
||||
|
|
@ -381,7 +387,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
.expect("global should have been cached (static)");
|
||||
match global_value.value {
|
||||
// FIXME: to_ptr()? might be too extreme here, static zsts might reach this under certain conditions
|
||||
Value::ByRef(ptr, _aligned) =>
|
||||
Value::ByRef { ptr, aligned: _aligned } =>
|
||||
// Alignment does not matter for this call
|
||||
self.memory.mark_static_initalized(ptr.to_ptr()?.alloc_id, mutable)?,
|
||||
Value::ByVal(val) => if let PrimVal::Ptr(ptr) = val {
|
||||
|
|
@ -439,7 +445,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
}
|
||||
|
||||
pub fn deallocate_local(&mut self, local: Option<Value>) -> EvalResult<'tcx> {
|
||||
if let Some(Value::ByRef(ptr, _aligned)) = local {
|
||||
if let Some(Value::ByRef { ptr, aligned: _ }) = local {
|
||||
trace!("deallocating local");
|
||||
let ptr = ptr.to_ptr()?;
|
||||
self.memory.dump_alloc(ptr.alloc_id);
|
||||
|
|
@ -609,7 +615,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
let operand_ty = self.operand_ty(operand);
|
||||
assert_eq!(self.type_size(operand_ty)?, Some(0));
|
||||
}
|
||||
let (offset, ty) = self.nonnull_offset_and_ty(dest_ty, nndiscr, discrfield)?;
|
||||
let (offset, TyAndPacked { ty, packed: _}) = self.nonnull_offset_and_ty(dest_ty, nndiscr, discrfield)?;
|
||||
// TODO: The packed flag is ignored
|
||||
|
||||
// FIXME(solson)
|
||||
let dest = self.force_allocation(dest)?.to_ptr()?;
|
||||
|
|
@ -702,7 +709,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
LvalueExtra::DowncastVariant(..) =>
|
||||
bug!("attempted to take a reference to an enum downcast lvalue"),
|
||||
};
|
||||
|
||||
self.write_value(val, dest, dest_ty)?;
|
||||
}
|
||||
|
||||
|
|
@ -745,7 +751,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
let src_ty = self.operand_ty(operand);
|
||||
if self.type_is_fat_ptr(src_ty) {
|
||||
match (src, self.type_is_fat_ptr(dest_ty)) {
|
||||
(Value::ByRef(..), _) |
|
||||
(Value::ByRef{..}, _) |
|
||||
(Value::ByValPair(..), true) => {
|
||||
self.write_value(src, dest, dest_ty)?;
|
||||
},
|
||||
|
|
@ -826,7 +832,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
ty: Ty<'tcx>,
|
||||
nndiscr: u64,
|
||||
discrfield: &[u32],
|
||||
) -> EvalResult<'tcx, (Size, Ty<'tcx>)> {
|
||||
) -> EvalResult<'tcx, (Size, TyAndPacked<'tcx>)> {
|
||||
// Skip the constant 0 at the start meant for LLVM GEP and the outer non-null variant
|
||||
let path = discrfield.iter().skip(2).map(|&i| i as usize);
|
||||
|
||||
|
|
@ -849,16 +855,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
mut offset: Size,
|
||||
mut ty: Ty<'tcx>,
|
||||
path: I,
|
||||
) -> EvalResult<'tcx, (Size, Ty<'tcx>)> {
|
||||
) -> EvalResult<'tcx, (Size, TyAndPacked<'tcx>)> {
|
||||
// Skip the initial 0 intended for LLVM GEP.
|
||||
let mut packed = false;
|
||||
for field_index in path {
|
||||
let field_offset = self.get_field_offset(ty, field_index)?;
|
||||
trace!("field_path_offset_and_ty: {}, {}, {:?}, {:?}", field_index, ty, field_offset, offset);
|
||||
ty = self.get_field_ty(ty, field_index)?;
|
||||
let field_ty = self.get_field_ty(ty, field_index)?;
|
||||
ty = field_ty.ty;
|
||||
packed = packed || field_ty.packed;
|
||||
offset = offset.checked_add(field_offset, &self.tcx.data_layout).unwrap();
|
||||
}
|
||||
|
||||
Ok((offset, ty))
|
||||
Ok((offset, TyAndPacked { ty, packed }))
|
||||
}
|
||||
fn get_fat_field(&self, pointee_ty: Ty<'tcx>, field_index: usize) -> EvalResult<'tcx, Ty<'tcx>> {
|
||||
match (field_index, &self.tcx.struct_tail(pointee_ty).sty) {
|
||||
|
|
@ -870,33 +879,46 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_field_ty(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult<'tcx, Ty<'tcx>> {
|
||||
/// Returns the field type and whether the field is packed
|
||||
pub fn get_field_ty(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult<'tcx, TyAndPacked<'tcx>> {
|
||||
match ty.sty {
|
||||
ty::TyAdt(adt_def, _) if adt_def.is_box() => self.get_fat_field(ty.boxed_ty(), field_index),
|
||||
ty::TyAdt(adt_def, _) if adt_def.is_box() =>
|
||||
Ok(TyAndPacked { ty: self.get_fat_field(ty.boxed_ty(), field_index)?, packed: false }),
|
||||
ty::TyAdt(adt_def, substs) if adt_def.is_enum() => {
|
||||
use rustc::ty::layout::Layout::*;
|
||||
match *self.type_layout(ty)? {
|
||||
RawNullablePointer { nndiscr, .. } |
|
||||
StructWrappedNullablePointer { nndiscr, .. } => Ok(adt_def.variants[nndiscr as usize].fields[field_index].ty(self.tcx, substs)),
|
||||
RawNullablePointer { nndiscr, .. } =>
|
||||
Ok(TyAndPacked { ty: adt_def.variants[nndiscr as usize].fields[field_index].ty(self.tcx, substs), packed: false }),
|
||||
StructWrappedNullablePointer { nndiscr, ref nonnull, .. } => {
|
||||
let ty = adt_def.variants[nndiscr as usize].fields[field_index].ty(self.tcx, substs);
|
||||
Ok(TyAndPacked { ty, packed: nonnull.packed })
|
||||
},
|
||||
_ => Err(EvalError::Unimplemented(format!("get_field_ty can't handle enum type: {:?}, {:?}", ty, ty.sty))),
|
||||
}
|
||||
}
|
||||
ty::TyAdt(adt_def, substs) => {
|
||||
Ok(adt_def.struct_variant().fields[field_index].ty(self.tcx, substs))
|
||||
let variant_def = adt_def.struct_variant();
|
||||
use rustc::ty::layout::Layout::*;
|
||||
match *self.type_layout(ty)? {
|
||||
Univariant { ref variant, .. } =>
|
||||
Ok(TyAndPacked { ty: variant_def.fields[field_index].ty(self.tcx, substs), packed: variant.packed }),
|
||||
_ => Err(EvalError::Unimplemented(format!("get_field_ty can't handle struct type: {:?}, {:?}", ty, ty.sty))),
|
||||
}
|
||||
}
|
||||
|
||||
ty::TyTuple(fields, _) => Ok(fields[field_index]),
|
||||
ty::TyTuple(fields, _) => Ok(TyAndPacked { ty: fields[field_index], packed: false }),
|
||||
|
||||
ty::TyRef(_, ref tam) |
|
||||
ty::TyRawPtr(ref tam) => self.get_fat_field(tam.ty, field_index),
|
||||
ty::TyRawPtr(ref tam) => Ok(TyAndPacked { ty: self.get_fat_field(tam.ty, field_index)?, packed: false }),
|
||||
|
||||
ty::TyArray(ref inner, _) => Ok(inner),
|
||||
ty::TyArray(ref inner, _) => Ok(TyAndPacked { ty: inner, packed: false }),
|
||||
|
||||
_ => Err(EvalError::Unimplemented(format!("can't handle type: {:?}, {:?}", ty, ty.sty))),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_field_offset(&self, ty: Ty<'tcx>, field_index: usize) -> EvalResult<'tcx, Size> {
|
||||
// Also see lvalue_field in lvalue.rs, which handles more cases but needs an actual value at the given type
|
||||
let layout = self.type_layout(ty)?;
|
||||
|
||||
use rustc::ty::layout::Layout::*;
|
||||
|
|
@ -1026,7 +1048,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
// -1 since we don't store the return value
|
||||
match self.stack[frame].locals[local.index() - 1] {
|
||||
None => return Err(EvalError::DeadLocal),
|
||||
Some(Value::ByRef(ptr, aligned)) => {
|
||||
Some(Value::ByRef { ptr, aligned }) => {
|
||||
Lvalue::Ptr { ptr, aligned, extra: LvalueExtra::None }
|
||||
},
|
||||
Some(val) => {
|
||||
|
|
@ -1044,7 +1066,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
Lvalue::Global(cid) => {
|
||||
let global_val = self.globals.get(&cid).expect("global not cached").clone();
|
||||
match global_val.value {
|
||||
Value::ByRef(ptr, aligned) =>
|
||||
Value::ByRef { ptr, aligned } =>
|
||||
Lvalue::Ptr { ptr, aligned, extra: LvalueExtra::None },
|
||||
_ => {
|
||||
let ptr = self.alloc_ptr_with_substs(global_val.ty, cid.instance.substs)?;
|
||||
|
|
@ -1070,7 +1092,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
/// ensures this Value is not a ByRef
|
||||
pub(super) fn follow_by_ref_value(&mut self, value: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> {
|
||||
match value {
|
||||
Value::ByRef(ptr, aligned) => {
|
||||
Value::ByRef { ptr, aligned } => {
|
||||
self.read_maybe_aligned(aligned, |ectx| ectx.read_value(ptr, ty))
|
||||
}
|
||||
other => Ok(other),
|
||||
|
|
@ -1079,7 +1101,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
|
||||
pub(super) fn value_to_primval(&mut self, value: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
|
||||
match self.follow_by_ref_value(value, ty)? {
|
||||
Value::ByRef(..) => bug!("follow_by_ref_value can't result in `ByRef`"),
|
||||
Value::ByRef{..} => bug!("follow_by_ref_value can't result in `ByRef`"),
|
||||
|
||||
Value::ByVal(primval) => {
|
||||
self.ensure_valid_value(primval, ty)?;
|
||||
|
|
@ -1145,7 +1167,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
|
||||
Lvalue::Ptr { ptr, extra, aligned } => {
|
||||
assert_eq!(extra, LvalueExtra::None);
|
||||
self.write_maybe_aligned(aligned,
|
||||
self.write_maybe_aligned_mut(aligned,
|
||||
|ectx| ectx.write_value_to_ptr(src_val, ptr, dest_ty))
|
||||
}
|
||||
|
||||
|
|
@ -1169,7 +1191,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
old_dest_val: Value,
|
||||
dest_ty: Ty<'tcx>,
|
||||
) -> EvalResult<'tcx> {
|
||||
if let Value::ByRef(dest_ptr, aligned) = old_dest_val {
|
||||
if let Value::ByRef { ptr: dest_ptr, aligned } = old_dest_val {
|
||||
// If the value is already `ByRef` (that is, backed by an `Allocation`),
|
||||
// then we must write the new value into this allocation, because there may be
|
||||
// other pointers into the allocation. These other pointers are logically
|
||||
|
|
@ -1177,10 +1199,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
//
|
||||
// Thus, it would be an error to replace the `ByRef` with a `ByVal`, unless we
|
||||
// knew for certain that there were no outstanding pointers to this allocation.
|
||||
self.write_maybe_aligned(aligned,
|
||||
self.write_maybe_aligned_mut(aligned,
|
||||
|ectx| ectx.write_value_to_ptr(src_val, dest_ptr, dest_ty))?;
|
||||
|
||||
} else if let Value::ByRef(src_ptr, aligned) = src_val {
|
||||
} else if let Value::ByRef { ptr: src_ptr, aligned } = src_val {
|
||||
// If the value is not `ByRef`, then we know there are no pointers to it
|
||||
// and we can simply overwrite the `Value` in the locals array directly.
|
||||
//
|
||||
|
|
@ -1192,7 +1214,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
// It is a valid optimization to attempt reading a primitive value out of the
|
||||
// source and write that into the destination without making an allocation, so
|
||||
// we do so here.
|
||||
self.read_maybe_aligned(aligned, |ectx| {
|
||||
self.read_maybe_aligned_mut(aligned, |ectx| {
|
||||
if let Ok(Some(src_val)) = ectx.try_read_value(src_ptr, dest_ty) {
|
||||
write_dest(ectx, src_val)?;
|
||||
} else {
|
||||
|
|
@ -1218,8 +1240,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
dest_ty: Ty<'tcx>,
|
||||
) -> EvalResult<'tcx> {
|
||||
match value {
|
||||
Value::ByRef(ptr, aligned) => {
|
||||
self.read_maybe_aligned(aligned, |ectx| ectx.copy(ptr, dest, dest_ty))
|
||||
Value::ByRef { ptr, aligned } => {
|
||||
self.read_maybe_aligned_mut(aligned, |ectx| ectx.copy(ptr, dest, dest_ty))
|
||||
},
|
||||
Value::ByVal(primval) => {
|
||||
let size = self.type_size(dest_ty)?.expect("dest type must be sized");
|
||||
|
|
@ -1236,20 +1258,27 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
ptr: MemoryPointer,
|
||||
mut ty: Ty<'tcx>
|
||||
) -> EvalResult<'tcx> {
|
||||
let mut packed = false;
|
||||
while self.get_field_count(ty)? == 1 {
|
||||
ty = self.get_field_ty(ty, 0)?;
|
||||
let field = self.get_field_ty(ty, 0)?;
|
||||
ty = field.ty;
|
||||
packed = packed || field.packed;
|
||||
}
|
||||
assert_eq!(self.get_field_count(ty)?, 2);
|
||||
let field_0 = self.get_field_offset(ty, 0)?.bytes();
|
||||
let field_1 = self.get_field_offset(ty, 1)?.bytes();
|
||||
let field_0 = self.get_field_offset(ty, 0)?;
|
||||
let field_1 = self.get_field_offset(ty, 1)?;
|
||||
let field_0_ty = self.get_field_ty(ty, 0)?;
|
||||
let field_1_ty = self.get_field_ty(ty, 1)?;
|
||||
let field_0_size = self.type_size(field_0_ty)?.expect("pair element type must be sized");
|
||||
let field_1_size = self.type_size(field_1_ty)?.expect("pair element type must be sized");
|
||||
let field_0_ptr = ptr.offset(field_0, &self)?.into();
|
||||
let field_1_ptr = ptr.offset(field_1, &self)?.into();
|
||||
self.memory.write_primval(field_0_ptr, a, field_0_size)?;
|
||||
self.memory.write_primval(field_1_ptr, b, field_1_size)?;
|
||||
assert_eq!(field_0_ty.packed, field_1_ty.packed, "the two fields must agree on being packed");
|
||||
packed = packed || field_0_ty.packed;
|
||||
let field_0_size = self.type_size(field_0_ty.ty)?.expect("pair element type must be sized");
|
||||
let field_1_size = self.type_size(field_1_ty.ty)?.expect("pair element type must be sized");
|
||||
let field_0_ptr = ptr.offset(field_0.bytes(), &self)?.into();
|
||||
let field_1_ptr = ptr.offset(field_1.bytes(), &self)?.into();
|
||||
self.write_maybe_aligned_mut(!packed,
|
||||
|ectx| ectx.memory.write_primval(field_0_ptr, a, field_0_size))?;
|
||||
self.write_maybe_aligned_mut(!packed,
|
||||
|ectx| ectx.memory.write_primval(field_1_ptr, b, field_1_size))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -1352,7 +1381,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) fn read_value(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> {
|
||||
pub(super) fn read_value(&self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> {
|
||||
if let Some(val) = self.try_read_value(ptr, ty)? {
|
||||
Ok(val)
|
||||
} else {
|
||||
|
|
@ -1376,7 +1405,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn try_read_value(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, Option<Value>> {
|
||||
fn try_read_value(&self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, Option<Value>> {
|
||||
use syntax::ast::FloatTy;
|
||||
|
||||
let val = match ty.sty {
|
||||
|
|
@ -1488,7 +1517,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
|
||||
match (&src_pointee_ty.sty, &dest_pointee_ty.sty) {
|
||||
(&ty::TyArray(_, length), &ty::TySlice(_)) => {
|
||||
let ptr = src.into_ptr(&mut self.memory)?;
|
||||
let ptr = src.into_ptr(&self.memory)?;
|
||||
// u64 cast is from usize to u64, which is always good
|
||||
self.write_value(ptr.to_value_with_len(length as u64), dest, dest_ty)
|
||||
}
|
||||
|
|
@ -1502,7 +1531,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
let trait_ref = data.principal().unwrap().with_self_ty(self.tcx, src_pointee_ty);
|
||||
let trait_ref = self.tcx.erase_regions(&trait_ref);
|
||||
let vtable = self.get_vtable(src_pointee_ty, trait_ref)?;
|
||||
let ptr = src.into_ptr(&mut self.memory)?;
|
||||
let ptr = src.into_ptr(&self.memory)?;
|
||||
self.write_value(ptr.to_value_with_vtable(vtable), dest, dest_ty)
|
||||
},
|
||||
|
||||
|
|
@ -1529,8 +1558,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
return self.unsize_into_ptr(src, src_ty, dest, dest_ty, src_ty.boxed_ty(), dest_ty.boxed_ty());
|
||||
}
|
||||
if self.ty_to_primval_kind(src_ty).is_ok() {
|
||||
let sty = self.get_field_ty(src_ty, 0)?;
|
||||
let dty = self.get_field_ty(dest_ty, 0)?;
|
||||
// TODO: We ignore the packed flag here
|
||||
let sty = self.get_field_ty(src_ty, 0)?.ty;
|
||||
let dty = self.get_field_ty(dest_ty, 0)?.ty;
|
||||
return self.unsize_into(src, sty, dest, dty);
|
||||
}
|
||||
// unsizing of generic struct with pointer fields
|
||||
|
|
@ -1545,7 +1575,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
//let src = adt::MaybeSizedValue::sized(src);
|
||||
//let dst = adt::MaybeSizedValue::sized(dst);
|
||||
let src_ptr = match src {
|
||||
Value::ByRef(ptr, true) => ptr,
|
||||
Value::ByRef { ptr, aligned: true } => ptr,
|
||||
// TODO: Is it possible for unaligned pointers to occur here?
|
||||
_ => bug!("expected aligned pointer, got {:?}", src),
|
||||
};
|
||||
|
|
@ -1592,7 +1622,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
Err(err) => {
|
||||
panic!("Failed to access local: {:?}", err);
|
||||
}
|
||||
Ok(Value::ByRef(ptr, aligned)) => match ptr.into_inner_primval() {
|
||||
Ok(Value::ByRef { ptr, aligned }) => match ptr.into_inner_primval() {
|
||||
PrimVal::Ptr(ptr) => {
|
||||
write!(msg, " by {}ref:", if aligned { "" } else { "unaligned " }).unwrap();
|
||||
allocs.push(ptr.alloc_id);
|
||||
|
|
|
|||
|
|
@ -196,7 +196,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
match lvalue {
|
||||
Lvalue::Ptr { ptr, extra, aligned } => {
|
||||
assert_eq!(extra, LvalueExtra::None);
|
||||
Ok(Value::ByRef(ptr, aligned))
|
||||
Ok(Value::ByRef { ptr, aligned })
|
||||
}
|
||||
Lvalue::Local { frame, local } => {
|
||||
self.stack[frame].get_local(local)
|
||||
|
|
@ -305,7 +305,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
assert_eq!(offset.bytes(), 0, "ByVal can only have 1 non zst field with offset 0");
|
||||
return Ok(base);
|
||||
},
|
||||
Value::ByRef(..) |
|
||||
Value::ByRef{..} |
|
||||
Value::ByValPair(..) |
|
||||
Value::ByVal(_) => self.force_allocation(base)?.to_ptr_extra_aligned(),
|
||||
},
|
||||
|
|
@ -315,7 +315,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
assert_eq!(offset.bytes(), 0, "ByVal can only have 1 non zst field with offset 0");
|
||||
return Ok(base);
|
||||
},
|
||||
Value::ByRef(..) |
|
||||
Value::ByRef{..} |
|
||||
Value::ByValPair(..) |
|
||||
Value::ByVal(_) => self.force_allocation(base)?.to_ptr_extra_aligned(),
|
||||
},
|
||||
|
|
@ -349,17 +349,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
Ok(Lvalue::Ptr { ptr, extra, aligned: aligned && !packed })
|
||||
}
|
||||
|
||||
pub(super) fn val_to_lvalue(&mut self, val: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Lvalue<'tcx>> {
|
||||
pub(super) fn val_to_lvalue(&self, val: Value, ty: Ty<'tcx>) -> EvalResult<'tcx, Lvalue<'tcx>> {
|
||||
Ok(match self.tcx.struct_tail(ty).sty {
|
||||
ty::TyDynamic(..) => {
|
||||
let (ptr, vtable) = val.into_ptr_vtable_pair(&mut self.memory)?;
|
||||
let (ptr, vtable) = val.into_ptr_vtable_pair(&self.memory)?;
|
||||
Lvalue::Ptr { ptr, extra: LvalueExtra::Vtable(vtable), aligned: true }
|
||||
},
|
||||
ty::TyStr | ty::TySlice(_) => {
|
||||
let (ptr, len) = val.into_slice(&mut self.memory)?;
|
||||
let (ptr, len) = val.into_slice(&self.memory)?;
|
||||
Lvalue::Ptr { ptr, extra: LvalueExtra::Length(len), aligned: true }
|
||||
},
|
||||
_ => Lvalue::Ptr { ptr: val.into_ptr(&mut self.memory)?, extra: LvalueExtra::None, aligned: true },
|
||||
_ => Lvalue::Ptr { ptr: val.into_ptr(&self.memory)?, extra: LvalueExtra::None, aligned: true },
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian, BigEndian};
|
||||
use std::collections::{btree_map, BTreeMap, HashMap, HashSet, VecDeque};
|
||||
use std::{fmt, iter, ptr, mem, io, ops};
|
||||
use std::cell::Cell;
|
||||
|
||||
use rustc::ty;
|
||||
use rustc::ty::layout::{self, TargetDataLayout, HasDataLayout};
|
||||
|
|
@ -266,8 +267,8 @@ pub struct Memory<'a, 'tcx> {
|
|||
|
||||
/// To avoid having to pass flags to every single memory access, we have some global state saying whether
|
||||
/// alignment checking is currently enforced for read and/or write accesses.
|
||||
reads_are_aligned: bool,
|
||||
writes_are_aligned: bool,
|
||||
reads_are_aligned: Cell<bool>,
|
||||
writes_are_aligned: Cell<bool>,
|
||||
|
||||
/// The current stack frame. Used to check accesses against locks.
|
||||
cur_frame: usize,
|
||||
|
|
@ -287,8 +288,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
|
|||
literal_alloc_cache: HashMap::new(),
|
||||
thread_local: BTreeMap::new(),
|
||||
next_thread_local: 0,
|
||||
reads_are_aligned: true,
|
||||
writes_are_aligned: true,
|
||||
reads_are_aligned: Cell::new(true),
|
||||
writes_are_aligned: Cell::new(true),
|
||||
cur_frame: usize::max_value(),
|
||||
}
|
||||
}
|
||||
|
|
@ -796,7 +797,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
|
|||
impl<'a, 'tcx> Memory<'a, 'tcx> {
|
||||
fn get_bytes_unchecked(&self, ptr: MemoryPointer, size: u64, align: u64) -> EvalResult<'tcx, &[u8]> {
|
||||
// Zero-sized accesses can use dangling pointers, but they still have to be aligned and non-NULL
|
||||
if self.reads_are_aligned {
|
||||
if self.reads_are_aligned.get() {
|
||||
self.check_align(ptr.into(), align)?;
|
||||
}
|
||||
if size == 0 {
|
||||
|
|
@ -813,7 +814,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
|
|||
|
||||
fn get_bytes_unchecked_mut(&mut self, ptr: MemoryPointer, size: u64, align: u64) -> EvalResult<'tcx, &mut [u8]> {
|
||||
// Zero-sized accesses can use dangling pointers, but they still have to be aligned and non-NULL
|
||||
if self.writes_are_aligned {
|
||||
if self.writes_are_aligned.get() {
|
||||
self.check_align(ptr.into(), align)?;
|
||||
}
|
||||
if size == 0 {
|
||||
|
|
@ -909,10 +910,10 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
|
|||
pub fn copy(&mut self, src: Pointer, dest: Pointer, size: u64, align: u64, nonoverlapping: bool) -> EvalResult<'tcx> {
|
||||
if size == 0 {
|
||||
// Empty accesses don't need to be valid pointers, but they should still be aligned
|
||||
if self.reads_are_aligned {
|
||||
if self.reads_are_aligned.get() {
|
||||
self.check_align(src, align)?;
|
||||
}
|
||||
if self.writes_are_aligned {
|
||||
if self.writes_are_aligned.get() {
|
||||
self.check_align(dest, align)?;
|
||||
}
|
||||
return Ok(());
|
||||
|
|
@ -968,7 +969,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
|
|||
pub fn read_bytes(&self, ptr: Pointer, size: u64) -> EvalResult<'tcx, &[u8]> {
|
||||
if size == 0 {
|
||||
// Empty accesses don't need to be valid pointers, but they should still be non-NULL
|
||||
if self.reads_are_aligned {
|
||||
if self.reads_are_aligned.get() {
|
||||
self.check_align(ptr, 1)?;
|
||||
}
|
||||
return Ok(&[]);
|
||||
|
|
@ -979,7 +980,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
|
|||
pub fn write_bytes(&mut self, ptr: Pointer, src: &[u8]) -> EvalResult<'tcx> {
|
||||
if src.is_empty() {
|
||||
// Empty accesses don't need to be valid pointers, but they should still be non-NULL
|
||||
if self.writes_are_aligned {
|
||||
if self.writes_are_aligned.get() {
|
||||
self.check_align(ptr, 1)?;
|
||||
}
|
||||
return Ok(());
|
||||
|
|
@ -992,7 +993,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
|
|||
pub fn write_repeat(&mut self, ptr: Pointer, val: u8, count: u64) -> EvalResult<'tcx> {
|
||||
if count == 0 {
|
||||
// Empty accesses don't need to be valid pointers, but they should still be non-NULL
|
||||
if self.writes_are_aligned {
|
||||
if self.writes_are_aligned.get() {
|
||||
self.check_align(ptr, 1)?;
|
||||
}
|
||||
return Ok(());
|
||||
|
|
@ -1399,23 +1400,36 @@ pub(crate) trait HasMemory<'a, 'tcx> {
|
|||
fn memory(&self) -> &Memory<'a, 'tcx>;
|
||||
|
||||
// These are not supposed to be overriden.
|
||||
fn read_maybe_aligned<F, T>(&mut self, aligned: bool, f: F) -> EvalResult<'tcx, T>
|
||||
where F: FnOnce(&mut Self) -> EvalResult<'tcx, T>
|
||||
fn read_maybe_aligned<F, T>(&self, aligned: bool, f: F) -> EvalResult<'tcx, T>
|
||||
where F: FnOnce(&Self) -> EvalResult<'tcx, T>
|
||||
{
|
||||
assert!(self.memory_mut().reads_are_aligned, "Unaligned reads must not be nested");
|
||||
self.memory_mut().reads_are_aligned = aligned;
|
||||
let old = self.memory().reads_are_aligned.get();
|
||||
// Do alignment checking if *all* nested calls say it has to be aligned.
|
||||
self.memory().reads_are_aligned.set(old && aligned);
|
||||
let t = f(self);
|
||||
self.memory_mut().reads_are_aligned = true;
|
||||
self.memory().reads_are_aligned.set(old);
|
||||
t
|
||||
}
|
||||
|
||||
fn write_maybe_aligned<F, T>(&mut self, aligned: bool, f: F) -> EvalResult<'tcx, T>
|
||||
fn read_maybe_aligned_mut<F, T>(&mut self, aligned: bool, f: F) -> EvalResult<'tcx, T>
|
||||
where F: FnOnce(&mut Self) -> EvalResult<'tcx, T>
|
||||
{
|
||||
assert!(self.memory_mut().writes_are_aligned, "Unaligned writes must not be nested");
|
||||
self.memory_mut().writes_are_aligned = aligned;
|
||||
let old = self.memory().reads_are_aligned.get();
|
||||
// Do alignment checking if *all* nested calls say it has to be aligned.
|
||||
self.memory().reads_are_aligned.set(old && aligned);
|
||||
let t = f(self);
|
||||
self.memory_mut().writes_are_aligned = true;
|
||||
self.memory().reads_are_aligned.set(old);
|
||||
t
|
||||
}
|
||||
|
||||
fn write_maybe_aligned_mut<F, T>(&mut self, aligned: bool, f: F) -> EvalResult<'tcx, T>
|
||||
where F: FnOnce(&mut Self) -> EvalResult<'tcx, T>
|
||||
{
|
||||
let old = self.memory().writes_are_aligned.get();
|
||||
// Do alignment checking if *all* nested calls say it has to be aligned.
|
||||
self.memory().writes_are_aligned.set(old && aligned);
|
||||
let t = f(self);
|
||||
self.memory().writes_are_aligned.set(old);
|
||||
t
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,13 +11,15 @@ use rustc::ty;
|
|||
use rustc::ty::layout::Layout;
|
||||
use rustc::ty::subst::Substs;
|
||||
|
||||
use error::{EvalResult, EvalError};
|
||||
use eval_context::{EvalContext, StackPopCleanup};
|
||||
use lvalue::{Global, GlobalId, Lvalue};
|
||||
use value::{Value, PrimVal};
|
||||
use syntax::codemap::Span;
|
||||
use syntax::ast::Mutability;
|
||||
|
||||
use error::{EvalResult, EvalError};
|
||||
use eval_context::{EvalContext, StackPopCleanup, TyAndPacked};
|
||||
use lvalue::{Global, GlobalId, Lvalue};
|
||||
use value::{Value, PrimVal};
|
||||
use memory::HasMemory;
|
||||
|
||||
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
pub fn inc_step_counter_and_check_limit(&mut self, n: u64) -> EvalResult<'tcx> {
|
||||
self.steps_remaining = self.steps_remaining.saturating_sub(n);
|
||||
|
|
@ -101,12 +103,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
|
||||
Layout::StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => {
|
||||
if variant_index as u64 != nndiscr {
|
||||
let (offset, ty) = self.nonnull_offset_and_ty(dest_ty, nndiscr, discrfield)?;
|
||||
let (offset, TyAndPacked { ty, packed }) = self.nonnull_offset_and_ty(dest_ty, nndiscr, discrfield)?;
|
||||
let nonnull = self.force_allocation(dest)?.to_ptr()?.offset(offset.bytes(), &self)?;
|
||||
trace!("struct wrapped nullable pointer type: {}", ty);
|
||||
// only the pointer part of a fat pointer is used for this space optimization
|
||||
let discr_size = self.type_size(ty)?.expect("bad StructWrappedNullablePointer discrfield");
|
||||
self.memory.write_uint(nonnull, 0, discr_size)?;
|
||||
self.write_maybe_aligned_mut(!packed, |ectx| ectx.memory.write_uint(nonnull, 0, discr_size))?;
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
|
||||
"arith_offset" => {
|
||||
let offset = self.value_to_primval(arg_vals[1], isize)?.to_i128()? as i64;
|
||||
let ptr = arg_vals[0].into_ptr(&mut self.memory)?;
|
||||
let ptr = arg_vals[0].into_ptr(&self.memory)?;
|
||||
let result_ptr = self.wrapping_pointer_offset(ptr, substs.type_at(0), offset)?;
|
||||
self.write_ptr(dest, result_ptr, dest_ty)?;
|
||||
}
|
||||
|
|
@ -61,7 +61,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
"atomic_load_acq" |
|
||||
"volatile_load" => {
|
||||
let ty = substs.type_at(0);
|
||||
let ptr = arg_vals[0].into_ptr(&mut self.memory)?;
|
||||
let ptr = arg_vals[0].into_ptr(&self.memory)?;
|
||||
self.write_value(Value::by_ref(ptr), dest, ty)?;
|
||||
}
|
||||
|
||||
|
|
@ -70,7 +70,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
"atomic_store_rel" |
|
||||
"volatile_store" => {
|
||||
let ty = substs.type_at(0);
|
||||
let dest = arg_vals[0].into_ptr(&mut self.memory)?;
|
||||
let dest = arg_vals[0].into_ptr(&self.memory)?;
|
||||
self.write_value_to_ptr(arg_vals[1], dest, ty)?;
|
||||
}
|
||||
|
||||
|
|
@ -80,12 +80,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
|
||||
_ if intrinsic_name.starts_with("atomic_xchg") => {
|
||||
let ty = substs.type_at(0);
|
||||
let ptr = arg_vals[0].into_ptr(&mut self.memory)?;
|
||||
let ptr = arg_vals[0].into_ptr(&self.memory)?;
|
||||
let change = self.value_to_primval(arg_vals[1], ty)?;
|
||||
let old = self.read_value(ptr, ty)?;
|
||||
let old = match old {
|
||||
Value::ByVal(val) => val,
|
||||
Value::ByRef(..) => bug!("just read the value, can't be byref"),
|
||||
Value::ByRef { .. } => bug!("just read the value, can't be byref"),
|
||||
Value::ByValPair(..) => bug!("atomic_xchg doesn't work with nonprimitives"),
|
||||
};
|
||||
self.write_primval(dest, old, ty)?;
|
||||
|
|
@ -94,13 +94,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
|
||||
_ if intrinsic_name.starts_with("atomic_cxchg") => {
|
||||
let ty = substs.type_at(0);
|
||||
let ptr = arg_vals[0].into_ptr(&mut self.memory)?;
|
||||
let ptr = arg_vals[0].into_ptr(&self.memory)?;
|
||||
let expect_old = self.value_to_primval(arg_vals[1], ty)?;
|
||||
let change = self.value_to_primval(arg_vals[2], ty)?;
|
||||
let old = self.read_value(ptr, ty)?;
|
||||
let old = match old {
|
||||
Value::ByVal(val) => val,
|
||||
Value::ByRef(..) => bug!("just read the value, can't be byref"),
|
||||
Value::ByRef { .. } => bug!("just read the value, can't be byref"),
|
||||
Value::ByValPair(..) => bug!("atomic_cxchg doesn't work with nonprimitives"),
|
||||
};
|
||||
let (val, _) = self.binary_op(mir::BinOp::Eq, old, ty, expect_old, ty)?;
|
||||
|
|
@ -115,12 +115,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
"atomic_xadd" | "atomic_xadd_acq" | "atomic_xadd_rel" | "atomic_xadd_acqrel" | "atomic_xadd_relaxed" |
|
||||
"atomic_xsub" | "atomic_xsub_acq" | "atomic_xsub_rel" | "atomic_xsub_acqrel" | "atomic_xsub_relaxed" => {
|
||||
let ty = substs.type_at(0);
|
||||
let ptr = arg_vals[0].into_ptr(&mut self.memory)?;
|
||||
let ptr = arg_vals[0].into_ptr(&self.memory)?;
|
||||
let change = self.value_to_primval(arg_vals[1], ty)?;
|
||||
let old = self.read_value(ptr, ty)?;
|
||||
let old = match old {
|
||||
Value::ByVal(val) => val,
|
||||
Value::ByRef(..) => bug!("just read the value, can't be byref"),
|
||||
Value::ByRef { .. } => bug!("just read the value, can't be byref"),
|
||||
Value::ByValPair(..) => bug!("atomic_xadd_relaxed doesn't work with nonprimitives"),
|
||||
};
|
||||
self.write_primval(dest, old, ty)?;
|
||||
|
|
@ -148,8 +148,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
// TODO: We do not even validate alignment for the 0-bytes case. libstd relies on this in vec::IntoIter::next.
|
||||
// Also see the write_bytes intrinsic.
|
||||
let elem_align = self.type_align(elem_ty)?;
|
||||
let src = arg_vals[0].into_ptr(&mut self.memory)?;
|
||||
let dest = arg_vals[1].into_ptr(&mut self.memory)?;
|
||||
let src = arg_vals[0].into_ptr(&self.memory)?;
|
||||
let dest = arg_vals[1].into_ptr(&self.memory)?;
|
||||
self.memory.copy(src, dest, count * elem_size, elem_align, intrinsic_name.ends_with("_nonoverlapping"))?;
|
||||
}
|
||||
}
|
||||
|
|
@ -176,7 +176,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
|
||||
"discriminant_value" => {
|
||||
let ty = substs.type_at(0);
|
||||
let adt_ptr = arg_vals[0].into_ptr(&mut self.memory)?.to_ptr()?;
|
||||
let adt_ptr = arg_vals[0].into_ptr(&self.memory)?.to_ptr()?;
|
||||
let discr_val = self.read_discriminant_value(adt_ptr, ty)?;
|
||||
self.write_primval(dest, PrimVal::Bytes(discr_val), dest_ty)?;
|
||||
}
|
||||
|
|
@ -251,10 +251,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
let size = self.type_size(dest_ty)?.expect("cannot zero unsized value");
|
||||
let init = |this: &mut Self, val: Value| {
|
||||
let zero_val = match val {
|
||||
Value::ByRef(ptr, aligned) => {
|
||||
Value::ByRef { ptr, aligned } => {
|
||||
// These writes have no alignment restriction anyway.
|
||||
this.memory.write_repeat(ptr, 0, size)?;
|
||||
Value::ByRef(ptr, aligned)
|
||||
Value::ByRef { ptr, aligned }
|
||||
},
|
||||
// TODO(solson): Revisit this, it's fishy to check for Undef here.
|
||||
Value::ByVal(PrimVal::Undef) => match this.ty_to_primval_kind(dest_ty) {
|
||||
|
|
@ -297,7 +297,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
|
||||
"move_val_init" => {
|
||||
let ty = substs.type_at(0);
|
||||
let ptr = arg_vals[0].into_ptr(&mut self.memory)?;
|
||||
let ptr = arg_vals[0].into_ptr(&self.memory)?;
|
||||
self.write_value_to_ptr(arg_vals[1], ptr, ty)?;
|
||||
}
|
||||
|
||||
|
|
@ -310,7 +310,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
|
||||
"offset" => {
|
||||
let offset = self.value_to_primval(arg_vals[1], isize)?.to_i128()? as i64;
|
||||
let ptr = arg_vals[0].into_ptr(&mut self.memory)?;
|
||||
let ptr = arg_vals[0].into_ptr(&self.memory)?;
|
||||
let result_ptr = self.pointer_offset(ptr, substs.type_at(0), offset)?;
|
||||
self.write_ptr(dest, result_ptr, dest_ty)?;
|
||||
}
|
||||
|
|
@ -399,7 +399,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
"transmute" => {
|
||||
let src_ty = substs.type_at(0);
|
||||
let ptr = self.force_allocation(dest)?.to_ptr()?;
|
||||
self.write_maybe_aligned(/*aligned*/false, |ectx| {
|
||||
self.write_maybe_aligned_mut(/*aligned*/false, |ectx| {
|
||||
ectx.write_value_to_ptr(arg_vals[0], ptr.into(), src_ty)
|
||||
})?;
|
||||
}
|
||||
|
|
@ -442,9 +442,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
let size = dest_layout.size(&self.tcx.data_layout).bytes();
|
||||
let uninit = |this: &mut Self, val: Value| {
|
||||
match val {
|
||||
Value::ByRef(ptr, aligned) => {
|
||||
Value::ByRef { ptr, aligned } => {
|
||||
this.memory.mark_definedness(ptr, size, false)?;
|
||||
Ok(Value::ByRef(ptr, aligned))
|
||||
Ok(Value::ByRef { ptr, aligned })
|
||||
},
|
||||
_ => Ok(Value::ByVal(PrimVal::Undef)),
|
||||
}
|
||||
|
|
@ -464,7 +464,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
let ty_align = self.type_align(ty)?;
|
||||
let val_byte = self.value_to_primval(arg_vals[1], u8)?.to_u128()? as u8;
|
||||
let size = self.type_size(ty)?.expect("write_bytes() type must be sized");
|
||||
let ptr = arg_vals[0].into_ptr(&mut self.memory)?;
|
||||
let ptr = arg_vals[0].into_ptr(&self.memory)?;
|
||||
let count = self.value_to_primval(arg_vals[2], usize)?.to_u64()?;
|
||||
if count > 0 {
|
||||
// HashMap relies on write_bytes on a NULL ptr with count == 0 to work
|
||||
|
|
@ -550,7 +550,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
Ok((size, align.abi()))
|
||||
}
|
||||
ty::TyDynamic(..) => {
|
||||
let (_, vtable) = value.into_ptr_vtable_pair(&mut self.memory)?;
|
||||
let (_, vtable) = value.into_ptr_vtable_pair(&self.memory)?;
|
||||
// the second entry in the vtable is the dynamic size of the object.
|
||||
self.read_size_and_align_from_vtable(vtable)
|
||||
}
|
||||
|
|
@ -558,7 +558,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
ty::TySlice(_) | ty::TyStr => {
|
||||
let elem_ty = ty.sequence_element_type(self.tcx);
|
||||
let elem_size = self.type_size(elem_ty)?.expect("slice element must be sized") as u64;
|
||||
let (_, len) = value.into_slice(&mut self.memory)?;
|
||||
let (_, len) = value.into_slice(&self.memory)?;
|
||||
let align = self.type_align(elem_ty)?;
|
||||
Ok((len * elem_size, align as u64))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@ use syntax::attr;
|
|||
use syntax::abi::Abi;
|
||||
|
||||
use error::{EvalError, EvalResult};
|
||||
use eval_context::{EvalContext, IntegerExt, StackPopCleanup, is_inhabited, self};
|
||||
use eval_context::{EvalContext, IntegerExt, StackPopCleanup, TyAndPacked, is_inhabited, self};
|
||||
use lvalue::Lvalue;
|
||||
use memory::{MemoryPointer, TlsKey, Kind};
|
||||
use memory::{MemoryPointer, TlsKey, Kind, HasMemory};
|
||||
use value::{PrimVal, Value};
|
||||
use rustc_data_structures::indexed_vec::Idx;
|
||||
use const_eval;
|
||||
|
|
@ -313,10 +313,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
if self.frame().mir.args_iter().count() == fields.len() + 1 {
|
||||
let offsets = variant.offsets.iter().map(|s| s.bytes());
|
||||
match arg_val {
|
||||
Value::ByRef(ptr, aligned) => {
|
||||
Value::ByRef { ptr, aligned } => {
|
||||
assert!(aligned, "Unaligned ByRef-values cannot occur as function arguments");
|
||||
for ((offset, ty), arg_local) in offsets.zip(fields).zip(arg_locals) {
|
||||
let arg = Value::ByRef(ptr.offset(offset, &self)?, true);
|
||||
let arg = Value::ByRef { ptr: ptr.offset(offset, &self)?, aligned: true};
|
||||
let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?;
|
||||
trace!("writing arg {:?} to {:?} (type: {})", arg, dest, ty);
|
||||
self.write_value(arg, dest, ty)?;
|
||||
|
|
@ -397,12 +397,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
},
|
||||
ty::InstanceDef::Virtual(_, idx) => {
|
||||
let ptr_size = self.memory.pointer_size();
|
||||
let (_, vtable) = self.eval_operand(&arg_operands[0])?.into_ptr_vtable_pair(&mut self.memory)?;
|
||||
let (_, vtable) = self.eval_operand(&arg_operands[0])?.into_ptr_vtable_pair(&self.memory)?;
|
||||
let fn_ptr = self.memory.read_ptr(vtable.offset(ptr_size * (idx as u64 + 3), &self)?)?;
|
||||
let instance = self.memory.get_fn(fn_ptr.to_ptr()?)?;
|
||||
let mut arg_operands = arg_operands.to_vec();
|
||||
let ty = self.operand_ty(&arg_operands[0]);
|
||||
let ty = self.get_field_ty(ty, 0)?;
|
||||
let ty = self.get_field_ty(ty, 0)?.ty; // TODO: packed flag is ignored
|
||||
match arg_operands[0] {
|
||||
mir::Operand::Consume(ref mut lval) => *lval = lval.clone().field(mir::Field::new(0), ty),
|
||||
_ => bug!("virtual call first arg cannot be a constant"),
|
||||
|
|
@ -487,12 +487,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
}
|
||||
|
||||
StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => {
|
||||
let (offset, ty) = self.nonnull_offset_and_ty(adt_ty, nndiscr, discrfield)?;
|
||||
let nonnull = adt_ptr.offset(offset.bytes(), self)?;
|
||||
let (offset, TyAndPacked { ty, packed }) = self.nonnull_offset_and_ty(adt_ty, nndiscr, discrfield)?;
|
||||
let nonnull = adt_ptr.offset(offset.bytes(), &*self)?;
|
||||
trace!("struct wrapped nullable pointer type: {}", ty);
|
||||
// only the pointer part of a fat pointer is used for this space optimization
|
||||
let discr_size = self.type_size(ty)?.expect("bad StructWrappedNullablePointer discrfield");
|
||||
self.read_nonnull_discriminant_value(nonnull, nndiscr as u128, discr_size)?
|
||||
self.read_maybe_aligned(!packed,
|
||||
|ectx| ectx.read_nonnull_discriminant_value(nonnull, nndiscr as u128, discr_size))?
|
||||
}
|
||||
|
||||
// The discriminant_value intrinsic returns 0 for non-sum types.
|
||||
|
|
@ -576,7 +577,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?;
|
||||
}
|
||||
"alloc::heap::::__rust_dealloc" => {
|
||||
let ptr = args[0].into_ptr(&mut self.memory)?.to_ptr()?;
|
||||
let ptr = args[0].into_ptr(&self.memory)?.to_ptr()?;
|
||||
let old_size = self.value_to_primval(args[1], usize)?.to_u64()?;
|
||||
let align = self.value_to_primval(args[2], usize)?.to_u64()?;
|
||||
if old_size == 0 {
|
||||
|
|
@ -588,7 +589,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
self.memory.deallocate(ptr, Some((old_size, align)), Kind::Rust)?;
|
||||
}
|
||||
"alloc::heap::::__rust_realloc" => {
|
||||
let ptr = args[0].into_ptr(&mut self.memory)?.to_ptr()?;
|
||||
let ptr = args[0].into_ptr(&self.memory)?.to_ptr()?;
|
||||
let old_size = self.value_to_primval(args[1], usize)?.to_u64()?;
|
||||
let old_align = self.value_to_primval(args[2], usize)?.to_u64()?;
|
||||
let new_size = self.value_to_primval(args[3], usize)?.to_u64()?;
|
||||
|
|
@ -664,7 +665,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
}
|
||||
|
||||
"free" => {
|
||||
let ptr = args[0].into_ptr(&mut self.memory)?;
|
||||
let ptr = args[0].into_ptr(&self.memory)?;
|
||||
if !ptr.is_null()? {
|
||||
self.memory.deallocate(ptr.to_ptr()?, None, Kind::C)?;
|
||||
}
|
||||
|
|
@ -678,8 +679,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
}
|
||||
|
||||
"dlsym" => {
|
||||
let _handle = args[0].into_ptr(&mut self.memory)?;
|
||||
let symbol = args[1].into_ptr(&mut self.memory)?.to_ptr()?;
|
||||
let _handle = args[0].into_ptr(&self.memory)?;
|
||||
let symbol = args[1].into_ptr(&self.memory)?.to_ptr()?;
|
||||
let symbol_name = self.memory.read_c_str(symbol)?;
|
||||
let err = format!("bad c unicode symbol: {:?}", symbol_name);
|
||||
let symbol_name = ::std::str::from_utf8(symbol_name).unwrap_or(&err);
|
||||
|
|
@ -690,8 +691,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
// fn __rust_maybe_catch_panic(f: fn(*mut u8), data: *mut u8, data_ptr: *mut usize, vtable_ptr: *mut usize) -> u32
|
||||
// We abort on panic, so not much is going on here, but we still have to call the closure
|
||||
let u8_ptr_ty = self.tcx.mk_mut_ptr(self.tcx.types.u8);
|
||||
let f = args[0].into_ptr(&mut self.memory)?.to_ptr()?;
|
||||
let data = args[1].into_ptr(&mut self.memory)?;
|
||||
let f = args[0].into_ptr(&self.memory)?.to_ptr()?;
|
||||
let data = args[1].into_ptr(&self.memory)?;
|
||||
let f_instance = self.memory.get_fn(f)?;
|
||||
self.write_null(dest, dest_ty)?;
|
||||
|
||||
|
|
@ -722,8 +723,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
}
|
||||
|
||||
"memcmp" => {
|
||||
let left = args[0].into_ptr(&mut self.memory)?;
|
||||
let right = args[1].into_ptr(&mut self.memory)?;
|
||||
let left = args[0].into_ptr(&self.memory)?;
|
||||
let right = args[1].into_ptr(&self.memory)?;
|
||||
let n = self.value_to_primval(args[2], usize)?.to_u64()?;
|
||||
|
||||
let result = {
|
||||
|
|
@ -742,7 +743,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
}
|
||||
|
||||
"memrchr" => {
|
||||
let ptr = args[0].into_ptr(&mut self.memory)?;
|
||||
let ptr = args[0].into_ptr(&self.memory)?;
|
||||
let val = self.value_to_primval(args[1], usize)?.to_u64()? as u8;
|
||||
let num = self.value_to_primval(args[2], usize)?.to_u64()?;
|
||||
if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().rev().position(|&c| c == val) {
|
||||
|
|
@ -754,7 +755,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
}
|
||||
|
||||
"memchr" => {
|
||||
let ptr = args[0].into_ptr(&mut self.memory)?;
|
||||
let ptr = args[0].into_ptr(&self.memory)?;
|
||||
let val = self.value_to_primval(args[1], usize)?.to_u64()? as u8;
|
||||
let num = self.value_to_primval(args[2], usize)?.to_u64()?;
|
||||
if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().position(|&c| c == val) {
|
||||
|
|
@ -767,7 +768,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
|
||||
"getenv" => {
|
||||
let result = {
|
||||
let name_ptr = args[0].into_ptr(&mut self.memory)?.to_ptr()?;
|
||||
let name_ptr = args[0].into_ptr(&self.memory)?.to_ptr()?;
|
||||
let name = self.memory.read_c_str(name_ptr)?;
|
||||
match self.env_vars.get(name) {
|
||||
Some(&var) => PrimVal::Ptr(var),
|
||||
|
|
@ -780,7 +781,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
"unsetenv" => {
|
||||
let mut success = None;
|
||||
{
|
||||
let name_ptr = args[0].into_ptr(&mut self.memory)?;
|
||||
let name_ptr = args[0].into_ptr(&self.memory)?;
|
||||
if !name_ptr.is_null()? {
|
||||
let name = self.memory.read_c_str(name_ptr.to_ptr()?)?;
|
||||
if !name.is_empty() && !name.contains(&b'=') {
|
||||
|
|
@ -801,8 +802,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
"setenv" => {
|
||||
let mut new = None;
|
||||
{
|
||||
let name_ptr = args[0].into_ptr(&mut self.memory)?;
|
||||
let value_ptr = args[1].into_ptr(&mut self.memory)?.to_ptr()?;
|
||||
let name_ptr = args[0].into_ptr(&self.memory)?;
|
||||
let value_ptr = args[1].into_ptr(&self.memory)?.to_ptr()?;
|
||||
let value = self.memory.read_c_str(value_ptr)?;
|
||||
if !name_ptr.is_null()? {
|
||||
let name = self.memory.read_c_str(name_ptr.to_ptr()?)?;
|
||||
|
|
@ -828,7 +829,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
|
||||
"write" => {
|
||||
let fd = self.value_to_primval(args[0], usize)?.to_u64()?;
|
||||
let buf = args[1].into_ptr(&mut self.memory)?;
|
||||
let buf = args[1].into_ptr(&self.memory)?;
|
||||
let n = self.value_to_primval(args[2], usize)?.to_u64()?;
|
||||
trace!("Called write({:?}, {:?}, {:?})", fd, buf, n);
|
||||
let result = if fd == 1 || fd == 2 { // stdout/stderr
|
||||
|
|
@ -845,7 +846,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
}
|
||||
|
||||
"strlen" => {
|
||||
let ptr = args[0].into_ptr(&mut self.memory)?.to_ptr()?;
|
||||
let ptr = args[0].into_ptr(&self.memory)?.to_ptr()?;
|
||||
let n = self.memory.read_c_str(ptr)?.len();
|
||||
self.write_primval(dest, PrimVal::Bytes(n as u128), dest_ty)?;
|
||||
}
|
||||
|
|
@ -888,10 +889,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
|
||||
// Hook pthread calls that go to the thread-local storage memory subsystem
|
||||
"pthread_key_create" => {
|
||||
let key_ptr = args[0].into_ptr(&mut self.memory)?;
|
||||
let key_ptr = args[0].into_ptr(&self.memory)?;
|
||||
|
||||
// Extract the function type out of the signature (that seems easier than constructing it ourselves...)
|
||||
let dtor = match args[1].into_ptr(&mut self.memory)?.into_inner_primval() {
|
||||
let dtor = match args[1].into_ptr(&self.memory)?.into_inner_primval() {
|
||||
PrimVal::Ptr(dtor_ptr) => Some(self.memory.get_fn(dtor_ptr)?),
|
||||
PrimVal::Bytes(0) => None,
|
||||
PrimVal::Bytes(_) => return Err(EvalError::ReadBytesAsPointer),
|
||||
|
|
@ -933,7 +934,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
|||
"pthread_setspecific" => {
|
||||
// The conversion into TlsKey here is a little fishy, but should work as long as usize >= libc::pthread_key_t
|
||||
let key = self.value_to_primval(args[0], usize)?.to_u64()? as TlsKey;
|
||||
let new_ptr = args[1].into_ptr(&mut self.memory)?;
|
||||
let new_ptr = args[1].into_ptr(&self.memory)?;
|
||||
self.memory.store_tls(key, new_ptr)?;
|
||||
|
||||
// Return success (0)
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ std::sync::atomic::AtomicBool::get_mut$|\
|
|||
fn validate_ptr(&mut self, val: Value, pointee_ty: Ty<'tcx>, re: Option<CodeExtent>, mutbl: Mutability, mode: ValidationMode) -> EvalResult<'tcx> {
|
||||
// Check alignment and non-NULLness
|
||||
let (_, align) = self.size_and_align_of_dst(pointee_ty, val)?;
|
||||
let ptr = val.into_ptr(&mut self.memory)?;
|
||||
let ptr = val.into_ptr(&self.memory)?;
|
||||
self.memory.check_align(ptr, align)?;
|
||||
|
||||
// Recurse
|
||||
|
|
@ -309,7 +309,7 @@ std::sync::atomic::AtomicBool::get_mut$|\
|
|||
self.validate_ptr(val, query.ty.boxed_ty(), query.re, query.mutbl, mode)
|
||||
}
|
||||
TyFnPtr(_sig) => {
|
||||
let ptr = self.read_lvalue(query.lval)?.into_ptr(&mut self.memory)?.to_ptr()?;
|
||||
let ptr = self.read_lvalue(query.lval)?.into_ptr(&self.memory)?.to_ptr()?;
|
||||
self.memory.get_fn(ptr)?;
|
||||
// TODO: Check if the signature matches (should be the same check as what terminator/mod.rs already does on call?).
|
||||
Ok(())
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ pub(super) fn f64_to_bytes(f: f64) -> u128 {
|
|||
/// operations and fat pointers. This idea was taken from rustc's trans.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum Value {
|
||||
ByRef(Pointer, bool),
|
||||
ByRef { ptr: Pointer, aligned: bool},
|
||||
ByVal(PrimVal),
|
||||
ByValPair(PrimVal, PrimVal),
|
||||
}
|
||||
|
|
@ -162,15 +162,15 @@ pub enum PrimValKind {
|
|||
impl<'a, 'tcx: 'a> Value {
|
||||
#[inline]
|
||||
pub(super) fn by_ref(ptr: Pointer) -> Self {
|
||||
Value::ByRef(ptr, true)
|
||||
Value::ByRef { ptr, aligned: true }
|
||||
}
|
||||
|
||||
/// Convert the value into a pointer (or a pointer-sized integer). If the value is a ByRef,
|
||||
/// this may have to perform a load.
|
||||
pub(super) fn into_ptr(&self, mem: &mut Memory<'a, 'tcx>) -> EvalResult<'tcx, Pointer> {
|
||||
pub(super) fn into_ptr(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, Pointer> {
|
||||
use self::Value::*;
|
||||
match *self {
|
||||
ByRef(ptr, aligned) => {
|
||||
ByRef { ptr, aligned } => {
|
||||
mem.read_maybe_aligned(aligned, |mem| mem.read_ptr(ptr.to_ptr()?) )
|
||||
},
|
||||
ByVal(ptr) | ByValPair(ptr, _) => Ok(ptr.into()),
|
||||
|
|
@ -179,11 +179,11 @@ impl<'a, 'tcx: 'a> Value {
|
|||
|
||||
pub(super) fn into_ptr_vtable_pair(
|
||||
&self,
|
||||
mem: &mut Memory<'a, 'tcx>
|
||||
mem: &Memory<'a, 'tcx>
|
||||
) -> EvalResult<'tcx, (Pointer, MemoryPointer)> {
|
||||
use self::Value::*;
|
||||
match *self {
|
||||
ByRef(ref_ptr, aligned) => {
|
||||
ByRef { ptr: ref_ptr, aligned } => {
|
||||
mem.read_maybe_aligned(aligned, |mem| {
|
||||
let ptr = mem.read_ptr(ref_ptr.to_ptr()?)?;
|
||||
let vtable = mem.read_ptr(ref_ptr.offset(mem.pointer_size(), mem.layout)?.to_ptr()?)?;
|
||||
|
|
@ -197,11 +197,11 @@ impl<'a, 'tcx: 'a> Value {
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) fn into_slice(&self, mem: &mut Memory<'a, 'tcx>) -> EvalResult<'tcx, (Pointer, u64)> {
|
||||
pub(super) fn into_slice(&self, mem: &Memory<'a, 'tcx>) -> EvalResult<'tcx, (Pointer, u64)> {
|
||||
use self::Value::*;
|
||||
match *self {
|
||||
ByRef(ref_ptr, aligned) => {
|
||||
mem.write_maybe_aligned(aligned, |mem| {
|
||||
ByRef { ptr: ref_ptr, aligned } => {
|
||||
mem.read_maybe_aligned(aligned, |mem| {
|
||||
let ptr = mem.read_ptr(ref_ptr.to_ptr()?)?;
|
||||
let len = mem.read_usize(ref_ptr.offset(mem.pointer_size(), mem.layout)?.to_ptr()?)?;
|
||||
Ok((ptr, len))
|
||||
|
|
|
|||
|
|
@ -40,7 +40,8 @@ fn test_unsizing() {
|
|||
|
||||
let arr = [1, 2, 3];
|
||||
let arr_unaligned: UnalignedPtr<[i32; 3]> = UnalignedPtr { data: &arr };
|
||||
let _uns: UnalignedPtr<[i32]> = arr_unaligned;
|
||||
let arr_unaligned: UnalignedPtr<[i32]> = arr_unaligned;
|
||||
let _unused = &arr_unaligned; // forcing an allocation, which could also yield "unaligned write"-errors
|
||||
}
|
||||
|
||||
fn main() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue