make force_allocation handle packed ByValPair

This commit is contained in:
Ralf Jung 2017-07-26 23:43:13 -07:00
parent f906c5458c
commit 4672cb7bde
6 changed files with 73 additions and 42 deletions

View file

@ -609,7 +609,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, 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 +703,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)?;
}
@ -826,7 +826,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
ty: Ty<'tcx>,
nndiscr: u64,
discrfield: &[u32],
) -> EvalResult<'tcx, (Size, Ty<'tcx>)> {
) -> EvalResult<'tcx, (Size, Ty<'tcx>, bool)> {
// 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 +849,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
mut offset: Size,
mut ty: Ty<'tcx>,
path: I,
) -> EvalResult<'tcx, (Size, Ty<'tcx>)> {
) -> EvalResult<'tcx, (Size, Ty<'tcx>, bool)> {
// 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.0;
packed = packed || field_ty.1;
offset = offset.checked_add(field_offset, &self.tcx.data_layout).unwrap();
}
Ok((offset, ty))
Ok((offset, 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 +873,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, (Ty<'tcx>, bool)> {
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((self.get_fat_field(ty.boxed_ty(), field_index)?, 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((adt_def.variants[nndiscr as usize].fields[field_index].ty(self.tcx, substs), false)),
StructWrappedNullablePointer { nndiscr, ref nonnull, .. } => {
let ty = adt_def.variants[nndiscr as usize].fields[field_index].ty(self.tcx, substs);
Ok((ty, 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((variant_def.fields[field_index].ty(self.tcx, substs), 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((fields[field_index], false)),
ty::TyRef(_, ref tam) |
ty::TyRawPtr(ref tam) => self.get_fat_field(tam.ty, field_index),
ty::TyRawPtr(ref tam) => Ok((self.get_fat_field(tam.ty, field_index)?, false)),
ty::TyArray(ref inner, _) => Ok(inner),
ty::TyArray(ref inner, _) => Ok((inner, 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::*;
@ -1236,20 +1252,28 @@ 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.0;
packed = packed || field.1;
}
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)?;
// The .1 components say whether the field is packed
assert_eq!(field_0_ty.1, field_1_ty.1, "the two fields must agree on being packed");
packed = packed || field_0_ty.1;
let field_0_size = self.type_size(field_0_ty.0)?.expect("pair element type must be sized");
let field_1_size = self.type_size(field_1_ty.0)?.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(!packed,
|ectx| ectx.memory.write_primval(field_0_ptr, a, field_0_size))?;
self.write_maybe_aligned(!packed,
|ectx| ectx.memory.write_primval(field_1_ptr, b, field_1_size))?;
Ok(())
}
@ -1529,8 +1553,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)?.0;
let dty = self.get_field_ty(dest_ty, 0)?.0;
return self.unsize_into(src, sty, dest, dty);
}
// unsizing of generic struct with pointer fields

View file

@ -295,6 +295,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
_ => bug!("field access on non-product type: {:?}", base_layout),
};
//trace!("Field {} of {:?} is at offset {}{}", field_index, base_ty, offset.bytes(),
// if packed { " (packed)" } else { "" });
// Do not allocate in trivial cases
let (base_ptr, base_extra, aligned) = match base {

View file

@ -1402,20 +1402,20 @@ pub(crate) trait HasMemory<'a, 'tcx> {
fn read_maybe_aligned<F, T>(&mut self, aligned: bool, f: F) -> EvalResult<'tcx, T>
where F: FnOnce(&mut 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_mut().reads_are_aligned;
self.memory_mut().reads_are_aligned = old && aligned;
let t = f(self);
self.memory_mut().reads_are_aligned = true;
self.memory_mut().reads_are_aligned = old;
t
}
fn write_maybe_aligned<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_mut().writes_are_aligned;
self.memory_mut().writes_are_aligned = old && aligned;
let t = f(self);
self.memory_mut().writes_are_aligned = true;
self.memory_mut().writes_are_aligned = old;
t
}
}

View file

@ -11,12 +11,14 @@ use rustc::ty;
use rustc::ty::layout::Layout;
use rustc::ty::subst::Substs;
use syntax::codemap::Span;
use syntax::ast::Mutability;
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 memory::HasMemory;
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
pub fn inc_step_counter_and_check_limit(&mut self, n: u64) -> EvalResult<'tcx> {
@ -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, 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(!packed, |ectx| ectx.memory.write_uint(nonnull, 0, discr_size))?;
}
},

View file

@ -9,7 +9,7 @@ use syntax::abi::Abi;
use error::{EvalError, EvalResult};
use eval_context::{EvalContext, IntegerExt, StackPopCleanup, 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;
@ -402,7 +402,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
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)?.0; // 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"),
@ -464,7 +464,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
Ok(false)
}
pub fn read_discriminant_value(&self, adt_ptr: MemoryPointer, adt_ty: Ty<'tcx>) -> EvalResult<'tcx, u128> {
pub fn read_discriminant_value(&mut self, adt_ptr: MemoryPointer, adt_ty: Ty<'tcx>) -> EvalResult<'tcx, u128> {
use rustc::ty::layout::Layout::*;
let adt_layout = self.type_layout(adt_ty)?;
//trace!("read_discriminant_value {:#?}", adt_layout);
@ -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, 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.

View file

@ -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() {