Simplify PrimValKind and remove a horrible hack.
This takes the `AllocId` out of PrimValKind, replacing it with a `relocation` field on `PrimVal`, which is closer to an earlier design for `PrimVal` I discussed with @eddyb. This commit prepares the code for removing the `PrimValKind` from `PrimVal` and making them more pure bitbags. The only code dealing with `PrimValKind` will be code making decisions like "what kind of operation do I need to do on these bits", like operators and casting. Transmutes of `PrimVal`s will become true no-ops, not even adjusting a `kind` field. This commit also removes my horrible `value_to_primval` hack that made an allocation for every `ByVal` passed in, so it could use `read_value` to get a `PrimVal` with the right kind. Now I just compute the `PrimValKind` from the `Ty` and re-tag the `PrimVal`. The code got slightly messier in some areas here, but I think a _lot_ of code will simplify in obvious ways once I remove the `kind` field from `PrimVal`. Gosh, if my commit messages aren't turning into essays these days.
This commit is contained in:
parent
330be7766f
commit
e7bcf35f8a
src
@ -21,8 +21,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
|
||||
Bool | Char | U8 | U16 | U32 | U64 => self.cast_int(val.bits, ty, false),
|
||||
|
||||
FnPtr(alloc) | Ptr(alloc) => {
|
||||
let ptr = Pointer::new(alloc, val.bits as usize);
|
||||
FnPtr | Ptr => {
|
||||
let ptr = val.expect_ptr("FnPtr- or Ptr-tagged PrimVal had no relocation");
|
||||
self.cast_ptr(ptr, ty)
|
||||
}
|
||||
}
|
||||
|
@ -972,7 +972,6 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
}
|
||||
|
||||
Deref => {
|
||||
use primval::PrimValKind::*;
|
||||
use interpreter::value::Value::*;
|
||||
|
||||
let val = match self.eval_and_read_lvalue(&proj.base)? {
|
||||
@ -981,22 +980,21 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
};
|
||||
|
||||
match val {
|
||||
ByValPair(
|
||||
PrimVal { kind: Ptr(ptr_alloc), bits: ptr_offset },
|
||||
PrimVal { kind: Ptr(vtable_alloc), bits: vtable_offset },
|
||||
) => {
|
||||
let ptr = Pointer::new(ptr_alloc, ptr_offset as usize);
|
||||
let vtable = Pointer::new(vtable_alloc, vtable_offset as usize);
|
||||
ByValPair(ptr, vtable)
|
||||
if ptr.try_as_ptr().is_some() && vtable.try_as_ptr().is_some()
|
||||
=> {
|
||||
let ptr = ptr.try_as_ptr().unwrap();
|
||||
let vtable = vtable.try_as_ptr().unwrap();
|
||||
(ptr, LvalueExtra::Vtable(vtable))
|
||||
}
|
||||
|
||||
ByValPair(PrimVal { kind: Ptr(alloc), bits: offset }, n) => {
|
||||
let ptr = Pointer::new(alloc, offset as usize);
|
||||
ByValPair(ptr, n) if ptr.try_as_ptr().is_some() => {
|
||||
let ptr = ptr.try_as_ptr().unwrap();
|
||||
(ptr, LvalueExtra::Length(n.expect_uint("slice length")))
|
||||
}
|
||||
|
||||
ByVal(PrimVal { kind: Ptr(alloc), bits: offset }) => {
|
||||
let ptr = Pointer::new(alloc, offset as usize);
|
||||
ByVal(ptr) if ptr.try_as_ptr().is_some() => {
|
||||
let ptr = ptr.try_as_ptr().unwrap();
|
||||
(ptr, LvalueExtra::None)
|
||||
}
|
||||
|
||||
@ -1122,39 +1120,20 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
Value::ByValPair(..) => bug!("value_to_primval can't work with fat pointers"),
|
||||
},
|
||||
|
||||
// FIXME(solson): This unnecessarily allocates to work around a new issue my `Value`
|
||||
// locals refactoring introduced. There is code that calls this function and expects to
|
||||
// get a PrimVal reflecting the specific type that it asked for, e.g. `PrimVal::Bool`
|
||||
// when it was asking for `TyBool`. This used to always work because it would go
|
||||
// through `read_value` which does the right thing.
|
||||
//
|
||||
// This is the comment and implementation from before my refactor:
|
||||
//
|
||||
// TODO(solson): Sanity-check the primval type against the input type.
|
||||
// Value::ByVal(primval) => Ok(primval),
|
||||
//
|
||||
// Turns out sanity-checking isn't enough now, and we need conversion.
|
||||
//
|
||||
// Now that we can possibly be reading a `ByVal` straight out of the locals vec, if the
|
||||
// user did something tricky like transmuting a `u8` to a `bool`, then we'll have a
|
||||
// `PrimVal::U8` and need to convert to `PrimVal::Bool`.
|
||||
//
|
||||
// I want to avoid handling the full set of conversions between `PrimVal`s, so for now
|
||||
// I will use this hack. I have a plan to change the representation of `PrimVal` to be
|
||||
// more like a small piece of memory tagged with a `PrimValKind`, which should make the
|
||||
// conversion easy and make the problem solveable using code already in `Memory`.
|
||||
Value::ByVal(primval) => {
|
||||
let ptr = self.alloc_ptr(ty)?;
|
||||
self.memory.write_primval(ptr, primval)?;
|
||||
let primval = self.value_to_primval(Value::ByRef(ptr), ty)?;
|
||||
self.memory.deallocate(ptr)?;
|
||||
Ok(primval)
|
||||
let new_primval = self.transmute_primval(primval, ty)?;
|
||||
self.ensure_valid_value(new_primval, ty)?;
|
||||
Ok(new_primval)
|
||||
}
|
||||
|
||||
Value::ByValPair(..) => bug!("value_to_primval can't work with fat pointers"),
|
||||
}
|
||||
}
|
||||
|
||||
fn transmute_primval(&self, val: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
|
||||
Ok(PrimVal { kind: self.ty_to_primval_kind(ty)?, ..val })
|
||||
}
|
||||
|
||||
fn write_primval(
|
||||
&mut self,
|
||||
dest: Lvalue,
|
||||
@ -1252,6 +1231,77 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn ty_to_primval_kind(&self, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimValKind> {
|
||||
use syntax::ast::FloatTy;
|
||||
|
||||
let kind = match ty.sty {
|
||||
ty::TyBool => PrimValKind::Bool,
|
||||
ty::TyChar => PrimValKind::Char,
|
||||
|
||||
ty::TyInt(int_ty) => {
|
||||
use syntax::ast::IntTy::*;
|
||||
let size = match int_ty {
|
||||
I8 => 1,
|
||||
I16 => 2,
|
||||
I32 => 4,
|
||||
I64 => 8,
|
||||
Is => self.memory.pointer_size(),
|
||||
};
|
||||
PrimValKind::from_int_size(size)
|
||||
}
|
||||
|
||||
ty::TyUint(uint_ty) => {
|
||||
use syntax::ast::UintTy::*;
|
||||
let size = match uint_ty {
|
||||
U8 => 1,
|
||||
U16 => 2,
|
||||
U32 => 4,
|
||||
U64 => 8,
|
||||
Us => self.memory.pointer_size(),
|
||||
};
|
||||
PrimValKind::from_uint_size(size)
|
||||
}
|
||||
|
||||
ty::TyFloat(FloatTy::F32) => PrimValKind::F32,
|
||||
ty::TyFloat(FloatTy::F64) => PrimValKind::F64,
|
||||
|
||||
ty::TyFnPtr(_) => PrimValKind::FnPtr,
|
||||
|
||||
ty::TyBox(ty) |
|
||||
ty::TyRef(_, ty::TypeAndMut { ty, .. }) |
|
||||
ty::TyRawPtr(ty::TypeAndMut { ty, .. }) if self.type_is_sized(ty) => PrimValKind::Ptr,
|
||||
|
||||
ty::TyAdt(..) => {
|
||||
use rustc::ty::layout::Layout::*;
|
||||
if let CEnum { discr, signed, .. } = *self.type_layout(ty) {
|
||||
let size = discr.size().bytes() as usize;
|
||||
if signed {
|
||||
PrimValKind::from_int_size(size)
|
||||
} else {
|
||||
PrimValKind::from_uint_size(size)
|
||||
}
|
||||
} else {
|
||||
bug!("primitive read of non-clike enum: {:?}", ty);
|
||||
}
|
||||
},
|
||||
|
||||
_ => bug!("primitive read of non-primitive type: {:?}", ty),
|
||||
};
|
||||
|
||||
Ok(kind)
|
||||
}
|
||||
|
||||
fn ensure_valid_value(&self, val: PrimVal, ty: Ty<'tcx>) -> EvalResult<'tcx, ()> {
|
||||
match ty.sty {
|
||||
ty::TyBool if val.bits > 1 => Err(EvalError::InvalidBool),
|
||||
|
||||
ty::TyChar if ::std::char::from_u32(val.bits as u32).is_none()
|
||||
=> Err(EvalError::InvalidChar(val.bits as u32 as u64)),
|
||||
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn read_value(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> {
|
||||
use syntax::ast::FloatTy;
|
||||
|
||||
|
@ -19,10 +19,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
dest_ty: Ty<'tcx>,
|
||||
dest_layout: &'tcx Layout,
|
||||
) -> EvalResult<'tcx, ()> {
|
||||
let args_ptrs: EvalResult<Vec<Value>> = args.iter()
|
||||
let arg_vals: EvalResult<Vec<Value>> = args.iter()
|
||||
.map(|arg| self.eval_operand(arg))
|
||||
.collect();
|
||||
let args_ptrs = args_ptrs?;
|
||||
let arg_vals = arg_vals?;
|
||||
let i32 = self.tcx.types.i32;
|
||||
let isize = self.tcx.types.isize;
|
||||
let usize = self.tcx.types.usize;
|
||||
@ -42,8 +42,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
|
||||
|
||||
"arith_offset" => {
|
||||
let ptr = args_ptrs[0].read_ptr(&self.memory)?;
|
||||
let offset = self.value_to_primval(args_ptrs[1], isize)?
|
||||
let ptr = arg_vals[0].read_ptr(&self.memory)?;
|
||||
let offset = self.value_to_primval(arg_vals[1], isize)?
|
||||
.expect_int("arith_offset second arg not isize");
|
||||
let new_ptr = ptr.offset(offset as isize);
|
||||
self.write_primval(dest, PrimVal::from_ptr(new_ptr))?;
|
||||
@ -51,23 +51,22 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
|
||||
"assume" => {
|
||||
let bool = self.tcx.types.bool;
|
||||
let cond = self.value_to_primval(args_ptrs[0], bool)?
|
||||
.expect_bool("assume arg not bool");
|
||||
let cond = self.value_to_primval(arg_vals[0], bool)?.try_as_bool()?;
|
||||
if !cond { return Err(EvalError::AssumptionNotHeld); }
|
||||
}
|
||||
|
||||
"atomic_load" |
|
||||
"volatile_load" => {
|
||||
let ty = substs.type_at(0);
|
||||
let ptr = args_ptrs[0].read_ptr(&self.memory)?;
|
||||
let ptr = arg_vals[0].read_ptr(&self.memory)?;
|
||||
self.write_value(Value::ByRef(ptr), dest, ty)?;
|
||||
}
|
||||
|
||||
"atomic_store" |
|
||||
"volatile_store" => {
|
||||
let ty = substs.type_at(0);
|
||||
let dest = args_ptrs[0].read_ptr(&self.memory)?;
|
||||
self.write_value_to_ptr(args_ptrs[1], dest, ty)?;
|
||||
let dest = arg_vals[0].read_ptr(&self.memory)?;
|
||||
self.write_value_to_ptr(arg_vals[1], dest, ty)?;
|
||||
}
|
||||
|
||||
"breakpoint" => unimplemented!(), // halt miri
|
||||
@ -78,9 +77,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
let elem_ty = substs.type_at(0);
|
||||
let elem_size = self.type_size(elem_ty);
|
||||
let elem_align = self.type_align(elem_ty);
|
||||
let src = args_ptrs[0].read_ptr(&self.memory)?;
|
||||
let dest = args_ptrs[1].read_ptr(&self.memory)?;
|
||||
let count = self.value_to_primval(args_ptrs[2], usize)?
|
||||
let src = arg_vals[0].read_ptr(&self.memory)?;
|
||||
let dest = arg_vals[1].read_ptr(&self.memory)?;
|
||||
let count = self.value_to_primval(arg_vals[2], usize)?
|
||||
.expect_uint("arith_offset second arg not isize");
|
||||
self.memory.copy(src, dest, count as usize * elem_size, elem_align)?;
|
||||
}
|
||||
@ -90,34 +89,34 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
"ctlz" |
|
||||
"bswap" => {
|
||||
let elem_ty = substs.type_at(0);
|
||||
let num = self.value_to_primval(args_ptrs[0], elem_ty)?;
|
||||
let num = self.value_to_primval(arg_vals[0], elem_ty)?;
|
||||
let num = numeric_intrinsic(intrinsic_name, num);
|
||||
self.write_primval(dest, num)?;
|
||||
}
|
||||
|
||||
"discriminant_value" => {
|
||||
let ty = substs.type_at(0);
|
||||
let adt_ptr = args_ptrs[0].read_ptr(&self.memory)?;
|
||||
let adt_ptr = arg_vals[0].read_ptr(&self.memory)?;
|
||||
let discr_val = self.read_discriminant_value(adt_ptr, ty)?;
|
||||
self.write_primval(dest, PrimVal::new(discr_val, PrimValKind::U64))?;
|
||||
}
|
||||
|
||||
"fabsf32" => {
|
||||
let f = self.value_to_primval(args_ptrs[2], f32)?
|
||||
let f = self.value_to_primval(arg_vals[2], f32)?
|
||||
.expect_f32("fabsf32 read non f32");
|
||||
self.write_primval(dest, PrimVal::from_f32(f.abs()))?;
|
||||
}
|
||||
|
||||
"fabsf64" => {
|
||||
let f = self.value_to_primval(args_ptrs[2], f64)?
|
||||
let f = self.value_to_primval(arg_vals[2], f64)?
|
||||
.expect_f64("fabsf64 read non f64");
|
||||
self.write_primval(dest, PrimVal::from_f64(f.abs()))?;
|
||||
}
|
||||
|
||||
"fadd_fast" => {
|
||||
let ty = substs.type_at(0);
|
||||
let a = self.value_to_primval(args_ptrs[0], ty)?;
|
||||
let b = self.value_to_primval(args_ptrs[0], ty)?;
|
||||
let a = self.value_to_primval(arg_vals[0], ty)?;
|
||||
let b = self.value_to_primval(arg_vals[0], ty)?;
|
||||
let result = primval::binary_op(mir::BinOp::Add, a, b)?;
|
||||
self.write_primval(dest, result.0)?;
|
||||
}
|
||||
@ -151,8 +150,8 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
|
||||
"move_val_init" => {
|
||||
let ty = substs.type_at(0);
|
||||
let ptr = args_ptrs[0].read_ptr(&self.memory)?;
|
||||
self.write_value_to_ptr(args_ptrs[1], ptr, ty)?;
|
||||
let ptr = arg_vals[0].read_ptr(&self.memory)?;
|
||||
self.write_value_to_ptr(arg_vals[1], ptr, ty)?;
|
||||
}
|
||||
|
||||
"needs_drop" => {
|
||||
@ -165,10 +164,10 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
"offset" => {
|
||||
let pointee_ty = substs.type_at(0);
|
||||
let pointee_size = self.type_size(pointee_ty) as isize;
|
||||
let offset = self.value_to_primval(args_ptrs[1], isize)?
|
||||
let offset = self.value_to_primval(arg_vals[1], isize)?
|
||||
.expect_int("offset second arg not isize");
|
||||
|
||||
let ptr = args_ptrs[0].read_ptr(&self.memory)?;
|
||||
let ptr = arg_vals[0].read_ptr(&self.memory)?;
|
||||
let result_ptr = ptr.offset(offset as isize * pointee_size);
|
||||
self.write_primval(dest, PrimVal::from_ptr(result_ptr))?;
|
||||
}
|
||||
@ -186,29 +185,29 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
}
|
||||
|
||||
"powif32" => {
|
||||
let f = self.value_to_primval(args_ptrs[0], f32)?
|
||||
let f = self.value_to_primval(arg_vals[0], f32)?
|
||||
.expect_f32("powif32 first arg not f32");
|
||||
let i = self.value_to_primval(args_ptrs[1], i32)?
|
||||
let i = self.value_to_primval(arg_vals[1], i32)?
|
||||
.expect_int("powif32 second arg not i32");
|
||||
self.write_primval(dest, PrimVal::from_f32(f.powi(i as i32)))?;
|
||||
}
|
||||
|
||||
"powif64" => {
|
||||
let f = self.value_to_primval(args_ptrs[0], f64)?
|
||||
let f = self.value_to_primval(arg_vals[0], f64)?
|
||||
.expect_f64("powif64 first arg not f64");
|
||||
let i = self.value_to_primval(args_ptrs[1], i32)?
|
||||
let i = self.value_to_primval(arg_vals[1], i32)?
|
||||
.expect_int("powif64 second arg not i32");
|
||||
self.write_primval(dest, PrimVal::from_f64(f.powi(i as i32)))?;
|
||||
}
|
||||
|
||||
"sqrtf32" => {
|
||||
let f = self.value_to_primval(args_ptrs[0], f32)?
|
||||
let f = self.value_to_primval(arg_vals[0], f32)?
|
||||
.expect_f32("sqrtf32 first arg not f32");
|
||||
self.write_primval(dest, PrimVal::from_f32(f.sqrt()))?;
|
||||
}
|
||||
|
||||
"sqrtf64" => {
|
||||
let f = self.value_to_primval(args_ptrs[0], f64)?
|
||||
let f = self.value_to_primval(arg_vals[0], f64)?
|
||||
.expect_f64("sqrtf64 first arg not f64");
|
||||
self.write_primval(dest, PrimVal::from_f64(f.sqrt()))?;
|
||||
}
|
||||
@ -222,7 +221,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
|
||||
"size_of_val" => {
|
||||
let ty = substs.type_at(0);
|
||||
let (size, _) = self.size_and_align_of_dst(ty, args_ptrs[0])?;
|
||||
let (size, _) = self.size_and_align_of_dst(ty, arg_vals[0])?;
|
||||
let size_val = self.usize_primval(size);
|
||||
self.write_primval(dest, size_val)?;
|
||||
}
|
||||
@ -239,8 +238,13 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
}
|
||||
|
||||
"transmute" => {
|
||||
let ty = substs.type_at(0);
|
||||
self.write_value(args_ptrs[0], dest, ty)?;
|
||||
let dest_ty = substs.type_at(1);
|
||||
let val = match arg_vals[0] {
|
||||
Value::ByVal(primval) =>
|
||||
Value::ByVal(self.transmute_primval(primval, dest_ty)?),
|
||||
v => v,
|
||||
};
|
||||
self.write_value(val, dest, dest_ty)?;
|
||||
}
|
||||
|
||||
"uninit" => {
|
||||
|
@ -35,8 +35,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
Goto { target } => self.goto_block(target),
|
||||
|
||||
If { ref cond, targets: (then_target, else_target) } => {
|
||||
let cond_val = self.eval_operand_to_primval(cond)?
|
||||
.expect_bool("TerminatorKind::If condition constant was not a bool");
|
||||
let cond_val = self.eval_operand_to_primval(cond)?.try_as_bool()?;
|
||||
self.goto_block(if cond_val { then_target } else { else_target });
|
||||
}
|
||||
|
||||
@ -116,8 +115,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
|
||||
}
|
||||
|
||||
Assert { ref cond, expected, ref msg, target, .. } => {
|
||||
let cond_val = self.eval_operand_to_primval(cond)?
|
||||
.expect_bool("TerminatorKind::Assert condition constant was not a bool");
|
||||
let cond_val = self.eval_operand_to_primval(cond)?.try_as_bool()?;
|
||||
if expected == cond_val {
|
||||
self.goto_block(target);
|
||||
} else {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use error::EvalResult;
|
||||
use memory::{Memory, Pointer};
|
||||
use primval::{PrimVal, PrimValKind};
|
||||
use primval::PrimVal;
|
||||
|
||||
/// A `Value` represents a single self-contained Rust value.
|
||||
///
|
||||
@ -23,10 +23,8 @@ impl<'a, 'tcx: 'a> Value {
|
||||
match *self {
|
||||
ByRef(ptr) => mem.read_ptr(ptr),
|
||||
|
||||
ByVal(PrimVal { kind: PrimValKind::Ptr(alloc), bits: offset }) |
|
||||
ByVal(PrimVal { kind: PrimValKind::FnPtr(alloc), bits: offset }) => {
|
||||
let ptr = Pointer::new(alloc, offset as usize);
|
||||
Ok(ptr)
|
||||
ByVal(ptr) if ptr.try_as_ptr().is_some() => {
|
||||
Ok(ptr.try_as_ptr().unwrap())
|
||||
}
|
||||
|
||||
ByValPair(..) => unimplemented!(),
|
||||
@ -46,12 +44,11 @@ impl<'a, 'tcx: 'a> Value {
|
||||
Ok((ptr, vtable))
|
||||
}
|
||||
|
||||
ByValPair(
|
||||
PrimVal { kind: PrimValKind::Ptr(ptr_alloc), bits: ptr_offset },
|
||||
PrimVal { kind: PrimValKind::Ptr(vtable_alloc), bits: vtable_offset },
|
||||
) => {
|
||||
let ptr = Pointer::new(ptr_alloc, ptr_offset as usize);
|
||||
let vtable = Pointer::new(vtable_alloc, vtable_offset as usize);
|
||||
ByValPair(ptr, vtable)
|
||||
if ptr.try_as_ptr().is_some() && vtable.try_as_ptr().is_some()
|
||||
=> {
|
||||
let ptr = ptr.try_as_ptr().unwrap();
|
||||
let vtable = vtable.try_as_ptr().unwrap();
|
||||
Ok((ptr, vtable))
|
||||
}
|
||||
|
||||
|
@ -532,25 +532,21 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write_primval(&mut self, ptr: Pointer, val: PrimVal) -> EvalResult<'tcx, ()> {
|
||||
use primval::PrimValKind::*;
|
||||
match val.kind {
|
||||
FnPtr(alloc_id) | Ptr(alloc_id) => {
|
||||
let p = Pointer::new(alloc_id, val.bits as usize);
|
||||
return self.write_ptr(ptr, p);
|
||||
}
|
||||
_ => {}
|
||||
pub fn write_primval(&mut self, dest: Pointer, val: PrimVal) -> EvalResult<'tcx, ()> {
|
||||
if let Some(ptr) = val.try_as_ptr() {
|
||||
return self.write_ptr(dest, ptr);
|
||||
}
|
||||
|
||||
use primval::PrimValKind::*;
|
||||
let (size, bits) = match val.kind {
|
||||
I8 | U8 | Bool => (1, val.bits as u8 as u64),
|
||||
I16 | U16 => (2, val.bits as u16 as u64),
|
||||
I32 | U32 | F32 | Char => (4, val.bits as u32 as u64),
|
||||
I64 | U64 | F64 => (8, val.bits),
|
||||
FnPtr(_) | Ptr(_) => bug!("handled above"),
|
||||
FnPtr | Ptr => bug!("handled above"),
|
||||
};
|
||||
|
||||
self.write_uint(ptr, bits, size)
|
||||
self.write_uint(dest, bits, size)
|
||||
}
|
||||
|
||||
pub fn read_bool(&self, ptr: Pointer) -> EvalResult<'tcx, bool> {
|
||||
|
129
src/primval.rs
129
src/primval.rs
@ -33,6 +33,15 @@ fn bits_to_bool(n: u64) -> bool {
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub struct PrimVal {
|
||||
pub bits: u64,
|
||||
|
||||
/// This field is initialized when the `PrimVal` represents a pointer into an `Allocation`. An
|
||||
/// `Allocation` in the `memory` module has a list of relocations, but a `PrimVal` is only
|
||||
/// large enough to contain one, hence the `Option`.
|
||||
pub relocation: Option<AllocId>,
|
||||
|
||||
// FIXME(solson): I think we can make this field unnecessary, or at least move it outside of
|
||||
// this struct. We can either match over `Ty`s or generate simple `PrimVal`s from `Ty`s and
|
||||
// match over those to decide which operations to perform on `PrimVal`s.
|
||||
pub kind: PrimValKind,
|
||||
}
|
||||
|
||||
@ -43,8 +52,8 @@ pub enum PrimValKind {
|
||||
F32, F64,
|
||||
Bool,
|
||||
Char,
|
||||
Ptr(AllocId),
|
||||
FnPtr(AllocId),
|
||||
Ptr,
|
||||
FnPtr,
|
||||
}
|
||||
|
||||
impl PrimValKind {
|
||||
@ -55,19 +64,43 @@ impl PrimValKind {
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_uint_size(size: usize) -> Self {
|
||||
match size {
|
||||
1 => PrimValKind::U8,
|
||||
2 => PrimValKind::U16,
|
||||
4 => PrimValKind::U32,
|
||||
8 => PrimValKind::U64,
|
||||
_ => bug!("can't make uint with size {}", size),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_int_size(size: usize) -> Self {
|
||||
match size {
|
||||
1 => PrimValKind::I8,
|
||||
2 => PrimValKind::I16,
|
||||
4 => PrimValKind::I32,
|
||||
8 => PrimValKind::I64,
|
||||
_ => bug!("can't make int with size {}", size),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PrimVal {
|
||||
pub fn new(bits: u64, kind: PrimValKind) -> Self {
|
||||
PrimVal { bits: bits, kind: kind }
|
||||
PrimVal { bits: bits, relocation: None, kind: kind }
|
||||
}
|
||||
|
||||
pub fn new_with_relocation(bits: u64, kind: PrimValKind, alloc_id: AllocId) -> Self {
|
||||
PrimVal { bits: bits, relocation: Some(alloc_id), kind: kind }
|
||||
}
|
||||
|
||||
pub fn from_ptr(ptr: Pointer) -> Self {
|
||||
PrimVal::new(ptr.offset as u64, PrimValKind::Ptr(ptr.alloc_id))
|
||||
PrimVal::new_with_relocation(ptr.offset as u64, PrimValKind::Ptr, ptr.alloc_id)
|
||||
}
|
||||
|
||||
pub fn from_fn_ptr(ptr: Pointer) -> Self {
|
||||
PrimVal::new(ptr.offset as u64, PrimValKind::FnPtr(ptr.alloc_id))
|
||||
PrimVal::new_with_relocation(ptr.offset as u64, PrimValKind::FnPtr, ptr.alloc_id)
|
||||
}
|
||||
|
||||
pub fn from_bool(b: bool) -> Self {
|
||||
@ -87,64 +120,58 @@ impl PrimVal {
|
||||
}
|
||||
|
||||
pub fn from_uint_with_size(n: u64, size: usize) -> Self {
|
||||
let kind = match size {
|
||||
1 => PrimValKind::U8,
|
||||
2 => PrimValKind::U16,
|
||||
4 => PrimValKind::U32,
|
||||
8 => PrimValKind::U64,
|
||||
_ => bug!("can't make uint ({}) with size {}", n, size),
|
||||
};
|
||||
PrimVal::new(n, kind)
|
||||
PrimVal::new(n, PrimValKind::from_uint_size(size))
|
||||
}
|
||||
|
||||
pub fn from_int_with_size(n: i64, size: usize) -> Self {
|
||||
let kind = match size {
|
||||
1 => PrimValKind::I8,
|
||||
2 => PrimValKind::I16,
|
||||
4 => PrimValKind::I32,
|
||||
8 => PrimValKind::I64,
|
||||
_ => bug!("can't make int ({}) with size {}", n, size),
|
||||
};
|
||||
PrimVal::new(n as u64, kind)
|
||||
PrimVal::new(n as u64, PrimValKind::from_int_size(size))
|
||||
}
|
||||
|
||||
pub fn to_f32(self) -> f32 {
|
||||
assert!(self.relocation.is_none());
|
||||
bits_to_f32(self.bits)
|
||||
}
|
||||
|
||||
pub fn to_f64(self) -> f64 {
|
||||
assert!(self.relocation.is_none());
|
||||
bits_to_f64(self.bits)
|
||||
}
|
||||
|
||||
pub fn try_as_ptr(self) -> Option<Pointer> {
|
||||
self.relocation.map(|alloc_id| {
|
||||
Pointer::new(alloc_id, self.bits as usize)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn expect_uint(self, error_msg: &str) -> u64 {
|
||||
if let Some(ptr) = self.try_as_ptr() {
|
||||
return ptr.to_int().expect("non abstract ptr") as u64
|
||||
}
|
||||
|
||||
use self::PrimValKind::*;
|
||||
match self.kind {
|
||||
U8 | U16 | U32 | U64 => self.bits,
|
||||
Ptr(alloc_id) => {
|
||||
let ptr = Pointer::new(alloc_id, self.bits as usize);
|
||||
ptr.to_int().expect("non abstract ptr") as u64
|
||||
}
|
||||
_ => bug!("{}", error_msg),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expect_int(self, error_msg: &str) -> i64 {
|
||||
if let Some(ptr) = self.try_as_ptr() {
|
||||
return ptr.to_int().expect("non abstract ptr") as i64
|
||||
}
|
||||
|
||||
use self::PrimValKind::*;
|
||||
match self.kind {
|
||||
I8 | I16 | I32 | I64 => self.bits as i64,
|
||||
Ptr(alloc_id) => {
|
||||
let ptr = Pointer::new(alloc_id, self.bits as usize);
|
||||
ptr.to_int().expect("non abstract ptr") as i64
|
||||
}
|
||||
_ => bug!("{}", error_msg),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expect_bool(self, error_msg: &str) -> bool {
|
||||
match (self.kind, self.bits) {
|
||||
(PrimValKind::Bool, 0) => false,
|
||||
(PrimValKind::Bool, 1) => true,
|
||||
_ => bug!("{}", error_msg),
|
||||
pub fn try_as_bool<'tcx>(self) -> EvalResult<'tcx, bool> {
|
||||
match self.bits {
|
||||
0 => Ok(false),
|
||||
1 => Ok(true),
|
||||
_ => Err(EvalError::InvalidBool),
|
||||
}
|
||||
}
|
||||
|
||||
@ -163,17 +190,12 @@ impl PrimVal {
|
||||
}
|
||||
|
||||
pub fn expect_ptr(self, error_msg: &str) -> Pointer {
|
||||
match self.kind {
|
||||
PrimValKind::Ptr(alloc_id) => Pointer::new(alloc_id, self.bits as usize),
|
||||
_ => bug!("{}", error_msg),
|
||||
}
|
||||
self.try_as_ptr().expect(error_msg)
|
||||
}
|
||||
|
||||
/// FIXME(solson): Refactored into a duplicate of `expect_ptr`. Investigate removal.
|
||||
pub fn expect_fn_ptr(self, error_msg: &str) -> Pointer {
|
||||
match self.kind {
|
||||
PrimValKind::FnPtr(alloc_id) => Pointer::new(alloc_id, self.bits as usize),
|
||||
_ => bug!("{}", error_msg),
|
||||
}
|
||||
self.try_as_ptr().expect(error_msg)
|
||||
}
|
||||
}
|
||||
|
||||
@ -255,22 +277,19 @@ pub fn binary_op<'tcx>(
|
||||
use rustc::mir::repr::BinOp::*;
|
||||
use self::PrimValKind::*;
|
||||
|
||||
match (left.kind, right.kind) {
|
||||
(FnPtr(_), Ptr(_)) |
|
||||
(Ptr(_), FnPtr(_)) => return Ok((unrelated_ptr_ops(bin_op)?, false)),
|
||||
|
||||
(Ptr(l_alloc), Ptr(r_alloc)) if l_alloc != r_alloc
|
||||
=> return Ok((unrelated_ptr_ops(bin_op)?, false)),
|
||||
|
||||
(FnPtr(l_alloc), FnPtr(r_alloc)) => {
|
||||
match bin_op {
|
||||
Eq => return Ok((PrimVal::from_bool(l_alloc == r_alloc), false)),
|
||||
Ne => return Ok((PrimVal::from_bool(l_alloc != r_alloc), false)),
|
||||
_ => {}
|
||||
match (left.try_as_ptr(), right.try_as_ptr()) {
|
||||
(Some(left_ptr), Some(right_ptr)) => {
|
||||
if left_ptr.alloc_id != right_ptr.alloc_id {
|
||||
return Ok((unrelated_ptr_ops(bin_op)?, false));
|
||||
}
|
||||
|
||||
// If the pointers are into the same allocation, fall through to the more general match
|
||||
// later, which will do comparisons on the `bits` fields, which are the pointer offsets
|
||||
// in this case.
|
||||
}
|
||||
|
||||
_ => {}
|
||||
(None, None) => {}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
|
||||
let (l, r) = (left.bits, right.bits);
|
||||
|
Loading…
x
Reference in New Issue
Block a user