From 2282e6b58253c5aad75fda48a8e9b15f0c9f62b1 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Tue, 14 Feb 2017 10:59:38 +0100 Subject: [PATCH 1/2] represent single field structs as their single field --- src/eval_context.rs | 54 +++++++++++++++++++++++++++++++++++-------- src/lvalue.rs | 8 ++++++- src/memory.rs | 16 +++++++++---- src/terminator/mod.rs | 2 +- 4 files changed, 64 insertions(+), 16 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 0ab6e85e38..123d1f5d47 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -312,14 +312,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { match global_value.value { Value::ByRef(ptr) => self.memory.mark_static_initalized(ptr.alloc_id, mutable)?, Value::ByVal(val) => if let PrimVal::Ptr(ptr) = val { - self.memory.mark_static_initalized(ptr.alloc_id, mutable)?; + self.memory.mark_inner_allocation(ptr.alloc_id, mutable)?; }, Value::ByValPair(val1, val2) => { if let PrimVal::Ptr(ptr) = val1 { - self.memory.mark_static_initalized(ptr.alloc_id, mutable)?; + self.memory.mark_inner_allocation(ptr.alloc_id, mutable)?; } if let PrimVal::Ptr(ptr) = val2 { - self.memory.mark_static_initalized(ptr.alloc_id, mutable)?; + self.memory.mark_inner_allocation(ptr.alloc_id, mutable)?; } }, } @@ -369,7 +369,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { discr_val: u128, variant_idx: usize, discr_size: u64, - ) -> EvalResult<'tcx> { + ) -> EvalResult<'tcx> + where J::IntoIter: ExactSizeIterator, + { // FIXME(solson) let dest_ptr = self.force_allocation(dest)?.to_ptr(); @@ -392,7 +394,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { dest: Lvalue<'tcx>, dest_ty: Ty<'tcx>, operands: J, - ) -> EvalResult<'tcx> { + ) -> EvalResult<'tcx> + where J::IntoIter: ExactSizeIterator, + { + if self.type_size(dest_ty)? == Some(0) { + // zst assigning is a nop + return Ok(()); + } + if self.ty_to_primval_kind(dest_ty).is_ok() { + let mut iter = operands.into_iter(); + assert_eq!(iter.len(), 1); + let (value, value_ty) = iter.next().unwrap().into_val_ty_pair(self)?; + return self.write_value(value, dest, value_ty); + } for (field_index, operand) in operands.into_iter().enumerate() { let (value, value_ty) = operand.into_val_ty_pair(self)?; let field_dest = self.lvalue_field(dest, field_index, dest_ty, value_ty)?; @@ -780,7 +794,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } - fn get_field_count(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, usize> { + pub fn get_field_count(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, usize> { let layout = self.type_layout(ty)?; use rustc::ty::layout::Layout::*; @@ -1037,8 +1051,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { a: PrimVal, b: PrimVal, ptr: Pointer, - ty: Ty<'tcx> + mut ty: Ty<'tcx> ) -> EvalResult<'tcx> { + while self.get_field_count(ty)? == 1 { + ty = self.get_field_ty(ty, 0)?; + } 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(); @@ -1094,7 +1111,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { ty::TyAdt(ref def, _) if def.is_box() => PrimValKind::Ptr, - ty::TyAdt(..) => { + ty::TyAdt(ref def, substs) => { use rustc::ty::layout::Layout::*; match *self.type_layout(ty)? { CEnum { discr, signed, .. } => { @@ -1117,6 +1134,18 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } } + // represent single field structs as their single field + Univariant { .. } => { + // enums with just one variant are no different, but `.struct_variant()` doesn't work for enums + let variant = &def.variants[0]; + // FIXME: also allow structs with only a single non zst field + if variant.fields.len() == 1 { + return self.ty_to_primval_kind(variant.fields[0].ty(self.tcx, substs)); + } else { + return Err(EvalError::TypeNotPrimitive(ty)); + } + } + _ => return Err(EvalError::TypeNotPrimitive(ty)), } } @@ -1305,8 +1334,11 @@ 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()); } - // FIXME(solson) - let dest = self.force_allocation(dest)?.to_ptr(); + 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)?; + return self.unsize_into(src, sty, dest, dty); + } // unsizing of generic struct with pointer fields // Example: `Arc` -> `Arc` // here we need to increase the size of every &T thin ptr field to a fat ptr @@ -1323,6 +1355,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => bug!("expected pointer, got {:?}", src), }; + // FIXME(solson) + let dest = self.force_allocation(dest)?.to_ptr(); let iter = src_fields.zip(dst_fields).enumerate(); for (i, (src_f, dst_f)) in iter { let src_fty = monomorphize_field_ty(self.tcx, src_f, substs_a); diff --git a/src/lvalue.rs b/src/lvalue.rs index d4d292cd4e..240b7ced22 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -223,10 +223,16 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { (self.force_allocation(base)?.to_ptr(), LvalueExtra::None) }, Value::ByVal(_) => { - assert_eq!(offset.bytes(), 0, "ByVal can only have 1 non zst field with offset 0"); + assert_eq!(field_index, 0, "ByVal can only have 1 non zst field with offset 0"); return Ok(base); }, Value::ByValPair(_, _) => { + let field_count = self.get_field_count(base_ty)?; + if field_count == 1 { + assert_eq!(field_index, 0, "{:?} has only one field", base_ty); + return Ok(base); + } + assert_eq!(field_count, 2); assert!(field_index < 2); return Ok(Lvalue::Local { frame, diff --git a/src/memory.rs b/src/memory.rs index 459a9bed41..86ca0083f2 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -673,13 +673,24 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { impl<'a, 'tcx> Memory<'a, 'tcx> { /// mark an allocation as being the entry point to a static (see `static_alloc` field) pub fn mark_static(&mut self, alloc_id: AllocId) { + trace!("mark_static: {:?}", alloc_id); if alloc_id != NEVER_ALLOC_ID && alloc_id != ZST_ALLOC_ID && !self.static_alloc.insert(alloc_id) { bug!("tried to mark an allocation ({:?}) as static twice", alloc_id); } } + /// mark an allocation pointed to by a static as static and initialized + pub fn mark_inner_allocation(&mut self, alloc: AllocId, mutable: bool) -> EvalResult<'tcx> { + // relocations into other statics are not "inner allocations" + if !self.static_alloc.contains(&alloc) { + self.mark_static_initalized(alloc, mutable)?; + } + Ok(()) + } + /// mark an allocation as static and initialized, either mutable or not pub fn mark_static_initalized(&mut self, alloc_id: AllocId, mutable: bool) -> EvalResult<'tcx> { + trace!("mark_static_initialized {:?}, mutable: {:?}", alloc_id, mutable); // do not use `self.get_mut(alloc_id)` here, because we might have already marked a // sub-element or have circular pointers (e.g. `Rc`-cycles) let relocations = match self.alloc_map.get_mut(&alloc_id) { @@ -699,10 +710,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> { }; // recurse into inner allocations for &alloc in relocations.values() { - // relocations into other statics are not "inner allocations" - if !self.static_alloc.contains(&alloc) { - self.mark_static_initalized(alloc, mutable)?; - } + self.mark_inner_allocation(alloc, mutable)?; } // put back the relocations self.alloc_map.get_mut(&alloc_id).expect("checked above").relocations = relocations; diff --git a/src/terminator/mod.rs b/src/terminator/mod.rs index 183a3f54fb..c258d56ded 100644 --- a/src/terminator/mod.rs +++ b/src/terminator/mod.rs @@ -490,7 +490,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { args.push((undef, field_ty)); } }, - _ => bug!("rust-call ABI tuple argument was {:?}", last), + _ => bug!("rust-call ABI tuple argument was {:?}, but {:?} were expected", last, fields), } } ty => bug!("expected tuple as last argument in function with 'rust-call' ABI, got {:?}", ty), From e2c5a6e64edf48a30fd7d593e90b1a97fe6a5aa7 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 15 Feb 2017 11:00:29 +0100 Subject: [PATCH 2/2] don't allocate for primvals --- src/lvalue.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/lvalue.rs b/src/lvalue.rs index 240b7ced22..2ca1f399af 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -219,8 +219,12 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { (ptr, LvalueExtra::None) }, Value::ByVal(PrimVal::Undef) => { - // FIXME: add some logic for when to not allocate - (self.force_allocation(base)?.to_ptr(), LvalueExtra::None) + // FIXME: allocate in fewer cases + if self.ty_to_primval_kind(base_ty).is_ok() { + return Ok(base); + } else { + (self.force_allocation(base)?.to_ptr(), LvalueExtra::None) + } }, Value::ByVal(_) => { assert_eq!(field_index, 0, "ByVal can only have 1 non zst field with offset 0");