Skip to content

lvalue references into fields of ValPair locals #139

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Feb 10, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 70 additions & 18 deletions src/eval_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
}
}
Expand Down Expand Up @@ -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) => {
Expand Down Expand Up @@ -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,
)
Expand Down Expand Up @@ -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);
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();
}
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);
}
Expand Down Expand Up @@ -1402,13 +1414,14 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
&mut self,
frame: usize,
local: mir::Local,
field: Option<usize>,
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)?;
Expand All @@ -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<usize>) -> 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<usize>, 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;
}
}
}

Expand Down
60 changes: 37 additions & 23 deletions src/lvalue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -114,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) {
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))
}
Expand All @@ -134,7 +126,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,
}
}
Expand All @@ -143,7 +135,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(&[]);
Expand All @@ -163,45 +155,41 @@ 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>> {
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 {
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)
Expand All @@ -210,6 +198,30 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
_ => bug!("field access on non-product type: {:?}", base_layout),
};

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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do we know this won't be hit?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We most likely can hit it with zst fields, but right now we don't create any ByValPairs for custom structs.

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) => {
let (_, align) = self.size_and_align_of_dst(base_ty, Value::ByValPair(PrimVal::Ptr(base_ptr), PrimVal::Ptr(tab)))?;
Expand All @@ -220,6 +232,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);
Expand Down
4 changes: 2 additions & 2 deletions src/terminator/intrinsic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)?,
Expand Down Expand Up @@ -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"),
Expand Down