From 31f3aabdd46e93bdbe88c08c1e62d5530dab182f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 10 Feb 2017 14:50:24 +0100 Subject: [PATCH 1/5] move some variables closer to their use site. --- src/lvalue.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/lvalue.rs b/src/lvalue.rs index 5ac5ad78b8..859ed32405 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -168,11 +168,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { field_ty: Ty<'tcx>, ) -> EvalResult<'tcx, Lvalue<'tcx>> { let base_layout = self.type_layout(base_ty)?; - // FIXME(solson) - let base = self.force_allocation(base)?; - let (base_ptr, base_extra) = base.to_ptr_and_extra(); - - let field_ty = self.monomorphize(field_ty, self.substs()); use rustc::ty::layout::Layout::*; let (offset, packed) = match *base_layout { @@ -181,6 +176,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { }, General { ref variants, .. } => { + let (_, base_extra) = base.to_ptr_and_extra(); if let LvalueExtra::DowncastVariant(variant_idx) = base_extra { // +1 for the discriminant, which is field 0 (variants[variant_idx].offsets[field + 1], variants[variant_idx].packed) @@ -210,6 +206,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => bug!("field access on non-product type: {:?}", base_layout), }; + // FIXME(solson) + let base = self.force_allocation(base)?; + let (base_ptr, base_extra) = base.to_ptr_and_extra(); + let offset = match base_extra { LvalueExtra::Vtable(tab) => { let (_, align) = self.size_and_align_of_dst(base_ty, Value::ByValPair(PrimVal::Ptr(base_ptr), PrimVal::Ptr(tab)))?; @@ -220,6 +220,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let ptr = base_ptr.offset(offset); + let field_ty = self.monomorphize(field_ty, self.substs()); + if packed { let size = self.type_size(field_ty)?.expect("packed struct must be sized"); self.memory.mark_packed(ptr, size); From 0b86d30594fe03858416139a940262d2277f53a0 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 10 Feb 2017 16:14:59 +0100 Subject: [PATCH 2/5] enable Lvalue::Local to refer to a ValPair field --- src/eval_context.rs | 88 +++++++++++++++++++++++++++++-------- src/lvalue.rs | 46 ++++++++++++++----- src/terminator/intrinsic.rs | 4 +- 3 files changed, 106 insertions(+), 32 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 739274ce39..114927b957 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -851,17 +851,26 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { lvalue: Lvalue<'tcx>, ) -> EvalResult<'tcx, Lvalue<'tcx>> { let new_lvalue = match lvalue { - Lvalue::Local { frame, local } => { - match self.stack[frame].get_local(local) { - Value::ByRef(ptr) => Lvalue::from_ptr(ptr), + Lvalue::Local { frame, local, field } => { + // -1 since we don't store the return value + match self.stack[frame].locals[local.index() - 1] { + Value::ByRef(ptr) => { + assert!(field.is_none()); + Lvalue::from_ptr(ptr) + }, val => { let ty = self.stack[frame].mir.local_decls[local].ty; let ty = self.monomorphize(ty, self.stack[frame].substs); let substs = self.stack[frame].substs; let ptr = self.alloc_ptr_with_substs(ty, substs)?; - self.stack[frame].set_local(local, Value::ByRef(ptr)); + self.stack[frame].locals[local.index() - 1] = Value::ByRef(ptr); self.write_value_to_ptr(val, ptr, ty)?; - Lvalue::from_ptr(ptr) + let lval = Lvalue::from_ptr(ptr); + if let Some((field, field_ty)) = field { + self.lvalue_field(lval, field, ty, field_ty)? + } else { + lval + } } } } @@ -924,8 +933,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { let size = self.type_size(dest_ty)?.expect("dest type must be sized"); self.memory.write_primval(ptr, val, size) } - Lvalue::Local { frame, local } => { - self.stack[frame].set_local(local, Value::ByVal(val)); + Lvalue::Local { frame, local, field } => { + self.stack[frame].set_local(local, field.map(|(i, _)| i), Value::ByVal(val)); Ok(()) } Lvalue::Global(cid) => { @@ -966,11 +975,11 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { self.write_value_to_ptr(src_val, ptr, dest_ty) } - Lvalue::Local { frame, local } => { - let dest = self.stack[frame].get_local(local); + Lvalue::Local { frame, local, field } => { + let dest = self.stack[frame].get_local(local, field.map(|(i, _)| i)); self.write_value_possibly_by_val( src_val, - |this, val| this.stack[frame].set_local(local, val), + |this, val| this.stack[frame].set_local(local, field.map(|(i, _)| i), val), dest, dest_ty, ) @@ -1355,16 +1364,19 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } pub(super) fn dump_local(&self, lvalue: Lvalue<'tcx>) { - if let Lvalue::Local { frame, local } = lvalue { + if let Lvalue::Local { frame, local, field } = lvalue { let mut allocs = Vec::new(); let mut msg = format!("{:?}", local); let last_frame = self.stack.len() - 1; if frame != last_frame { write!(msg, " ({} frames up)", last_frame - frame).unwrap(); } + if let Some(field) = field { + write!(msg, " (field {:?})", field).unwrap(); + } write!(msg, ":").unwrap(); - match self.stack[frame].get_local(local) { + match self.stack[frame].get_local(local, field.map(|(i, _)| i)) { Value::ByRef(ptr) => { allocs.push(ptr.alloc_id); } @@ -1402,13 +1414,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { &mut self, frame: usize, local: mir::Local, + field: Option, f: F, ) -> EvalResult<'tcx> where F: FnOnce(&mut Self, Value) -> EvalResult<'tcx, Value>, { - let val = self.stack[frame].get_local(local); + let val = self.stack[frame].get_local(local, field); let new_val = f(self, val)?; - self.stack[frame].set_local(local, new_val); + self.stack[frame].set_local(local, field, new_val); // FIXME(solson): Run this when setting to Undef? (See previous version of this code.) // if let Value::ByRef(ptr) = self.stack[frame].get_local(local) { // self.memory.deallocate(ptr)?; @@ -1418,14 +1431,53 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } impl<'tcx> Frame<'tcx> { - pub fn get_local(&self, local: mir::Local) -> Value { + pub fn get_local(&self, local: mir::Local, field: Option) -> Value { // Subtract 1 because we don't store a value for the ReturnPointer, the local with index 0. - self.locals[local.index() - 1] + if let Some(field) = field { + match self.locals[local.index() - 1] { + Value::ByRef(_) => bug!("can't have lvalue fields for ByRef"), + val @ Value::ByVal(_) => { + assert_eq!(field, 0); + val + }, + Value::ByValPair(a, b) => { + match field { + 0 => Value::ByVal(a), + 1 => Value::ByVal(b), + _ => bug!("ByValPair has only two fields, tried to access {}", field), + } + }, + } + } else { + self.locals[local.index() - 1] + } } - fn set_local(&mut self, local: mir::Local, value: Value) { + fn set_local(&mut self, local: mir::Local, field: Option, value: Value) { // Subtract 1 because we don't store a value for the ReturnPointer, the local with index 0. - self.locals[local.index() - 1] = value; + if let Some(field) = field { + match self.locals[local.index() - 1] { + Value::ByRef(_) => bug!("can't have lvalue fields for ByRef"), + Value::ByVal(_) => { + assert_eq!(field, 0); + self.set_local(local, None, value); + }, + Value::ByValPair(a, b) => { + let prim = match value { + Value::ByRef(_) => bug!("can't set ValPair field to ByRef"), + Value::ByVal(val) => val, + Value::ByValPair(_, _) => bug!("can't set ValPair field to ValPair"), + }; + match field { + 0 => self.set_local(local, None, Value::ByValPair(prim, b)), + 1 => self.set_local(local, None, Value::ByValPair(a, prim)), + _ => bug!("ByValPair has only two fields, tried to access {}", field), + } + }, + } + } else { + self.locals[local.index() - 1] = value; + } } } diff --git a/src/lvalue.rs b/src/lvalue.rs index 859ed32405..6a4bfb3c7d 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -23,6 +23,8 @@ pub enum Lvalue<'tcx> { Local { frame: usize, local: mir::Local, + /// Optionally, this lvalue can point to a field of the stack value + field: Option<(usize, Ty<'tcx>)>, }, /// An lvalue referring to a global @@ -116,7 +118,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn eval_and_read_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Value> { if let mir::Lvalue::Projection(ref proj) = *lvalue { if let mir::Lvalue::Local(index) = proj.base { - if let Value::ByValPair(a, b) = self.frame().get_local(index) { + if let Value::ByValPair(a, b) = self.frame().get_local(index, None) { if let mir::ProjectionElem::Field(ref field, _) = proj.elem { let val = [a, b][field.index()]; return Ok(Value::ByVal(val)); @@ -134,7 +136,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { assert_eq!(extra, LvalueExtra::None); Value::ByRef(ptr) } - Lvalue::Local { frame, local } => self.stack[frame].get_local(local), + Lvalue::Local { frame, local, field } => self.stack[frame].get_local(local, field.map(|(i, _)| i)), Lvalue::Global(cid) => self.globals.get(&cid).expect("global not cached").value, } } @@ -143,7 +145,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use rustc::mir::Lvalue::*; let lvalue = match *mir_lvalue { Local(mir::RETURN_POINTER) => self.frame().return_lvalue, - Local(local) => Lvalue::Local { frame: self.stack.len() - 1, local }, + Local(local) => Lvalue::Local { frame: self.stack.len() - 1, local, field: None }, Static(def_id) => { let substs = self.tcx.intern_substs(&[]); @@ -163,7 +165,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub fn lvalue_field( &mut self, base: Lvalue<'tcx>, - field: usize, + field_index: usize, base_ty: Ty<'tcx>, field_ty: Ty<'tcx>, ) -> EvalResult<'tcx, Lvalue<'tcx>> { @@ -172,32 +174,32 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { use rustc::ty::layout::Layout::*; let (offset, packed) = match *base_layout { Univariant { ref variant, .. } => { - (variant.offsets[field], variant.packed) + (variant.offsets[field_index], variant.packed) }, General { ref variants, .. } => { let (_, base_extra) = base.to_ptr_and_extra(); if let LvalueExtra::DowncastVariant(variant_idx) = base_extra { // +1 for the discriminant, which is field 0 - (variants[variant_idx].offsets[field + 1], variants[variant_idx].packed) + (variants[variant_idx].offsets[field_index + 1], variants[variant_idx].packed) } else { bug!("field access on enum had no variant index"); } } RawNullablePointer { .. } => { - assert_eq!(field, 0); + assert_eq!(field_index, 0); return Ok(base); } StructWrappedNullablePointer { ref nonnull, .. } => { - (nonnull.offsets[field], nonnull.packed) + (nonnull.offsets[field_index], nonnull.packed) } UntaggedUnion { .. } => return Ok(base), Vector { element, count } => { - let field = field as u64; + let field = field_index as u64; assert!(field < count); let elem_size = element.size(&self.tcx.data_layout).bytes(); (Size::from_bytes(field * elem_size), false) @@ -206,9 +208,29 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { _ => bug!("field access on non-product type: {:?}", base_layout), }; - // FIXME(solson) - let base = self.force_allocation(base)?; - let (base_ptr, base_extra) = base.to_ptr_and_extra(); + let (base_ptr, base_extra) = match base { + Lvalue::Ptr { ptr, extra } => (ptr, extra), + Lvalue::Local { frame, local, field } => match self.stack[frame].get_local(local, field.map(|(i, _)| i)) { + Value::ByRef(ptr) => { + assert!(field.is_none(), "local can't be ByRef and have a field offset"); + (ptr, LvalueExtra::None) + }, + Value::ByVal(_) => { + assert_eq!(offset.bytes(), 0, "ByVal can only have 1 non zst field with offset 0"); + return Ok(base); + }, + Value::ByValPair(_, _) => { + assert!(field_index < 2); + return Ok(Lvalue::Local { + frame, + local, + field: Some((field_index, field_ty)), + }); + }, + }, + // FIXME: do for globals what we did for locals + Lvalue::Global(_) => self.force_allocation(base)?.to_ptr_and_extra(), + }; let offset = match base_extra { LvalueExtra::Vtable(tab) => { diff --git a/src/terminator/intrinsic.rs b/src/terminator/intrinsic.rs index a2798790fe..f7e34d2bc6 100644 --- a/src/terminator/intrinsic.rs +++ b/src/terminator/intrinsic.rs @@ -254,7 +254,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { Ok(zero_val) }; match dest { - Lvalue::Local { frame, local } => self.modify_local(frame, local, init)?, + Lvalue::Local { frame, local, field } => self.modify_local(frame, local, field.map(|(i, _)| i), init)?, Lvalue::Ptr { ptr, extra: LvalueExtra::None } => self.memory.write_repeat(ptr, 0, size)?, Lvalue::Ptr { .. } => bug!("init intrinsic tried to write to fat ptr target"), Lvalue::Global(cid) => self.modify_global(cid, init)?, @@ -386,7 +386,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { } }; match dest { - Lvalue::Local { frame, local } => self.modify_local(frame, local, uninit)?, + Lvalue::Local { frame, local, field } => self.modify_local(frame, local, field.map(|(i, _)| i), uninit)?, Lvalue::Ptr { ptr, extra: LvalueExtra::None } => self.memory.mark_definedness(ptr, size, false)?, Lvalue::Ptr { .. } => bug!("uninit intrinsic tried to write to fat ptr target"), From 1e7481f96e2b17c269c2f96b6820efa15971d30f Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 10 Feb 2017 16:26:59 +0100 Subject: [PATCH 3/5] remove a hack that is now useless --- src/lvalue.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/lvalue.rs b/src/lvalue.rs index 6a4bfb3c7d..f3f8c3fde0 100644 --- a/src/lvalue.rs +++ b/src/lvalue.rs @@ -116,16 +116,6 @@ impl<'tcx> Global<'tcx> { impl<'a, 'tcx> EvalContext<'a, 'tcx> { pub(super) fn eval_and_read_lvalue(&mut self, lvalue: &mir::Lvalue<'tcx>) -> EvalResult<'tcx, Value> { - if let mir::Lvalue::Projection(ref proj) = *lvalue { - if let mir::Lvalue::Local(index) = proj.base { - if let Value::ByValPair(a, b) = self.frame().get_local(index, None) { - if let mir::ProjectionElem::Field(ref field, _) = proj.elem { - let val = [a, b][field.index()]; - return Ok(Value::ByVal(val)); - } - } - } - } let lvalue = self.eval_lvalue(lvalue)?; Ok(self.read_lvalue(lvalue)) } From 523c1877d98c5e0796f745c84328b2fa2864639a Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 10 Feb 2017 21:59:29 +0100 Subject: [PATCH 4/5] print local fields as `_2.1` instead of `_2 (field 1)` --- src/eval_context.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 114927b957..280117a424 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -1367,13 +1367,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if let Lvalue::Local { frame, local, field } = lvalue { let mut allocs = Vec::new(); let mut msg = format!("{:?}", local); + if let Some(field) = field { + write!(msg, ".{}", field).unwrap(); + } let last_frame = self.stack.len() - 1; if frame != last_frame { write!(msg, " ({} frames up)", last_frame - frame).unwrap(); } - if let Some(field) = field { - write!(msg, " (field {:?})", field).unwrap(); - } write!(msg, ":").unwrap(); match self.stack[frame].get_local(local, field.map(|(i, _)| i)) { From 35cf19f38a5aef8657b53f15b2df69c3a7080491 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 10 Feb 2017 22:15:30 +0100 Subject: [PATCH 5/5] only print the index part --- src/eval_context.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/eval_context.rs b/src/eval_context.rs index 280117a424..d2d33610f2 100644 --- a/src/eval_context.rs +++ b/src/eval_context.rs @@ -1367,7 +1367,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> { if let Lvalue::Local { frame, local, field } = lvalue { let mut allocs = Vec::new(); let mut msg = format!("{:?}", local); - if let Some(field) = field { + if let Some((field, _)) = field { write!(msg, ".{}", field).unwrap(); } let last_frame = self.stack.len() - 1;