Select Git revision
Forked from
Per Lindgren / e7020e_2019
Source project has a limited visibility.
-
Per Lindgren authored
bt broken? Cargo.toml no nightly dep more examples preLaunchTask crash re-added more preLaunchTasks example/device and SVD openocd.gdb comment fixed and ext itm launch config added bare0 bare1 bare1 bare2 and bare3 bare2 and bare3 bare2 and bare3 bare4 and bare5 bare6 wip bare6 wip serial wip serial wip bare7 bare8 bare8 to fresk to fresk to fresk to fresk bare8, 9 to fresk bare7 polishing bare9 rtfm_blinky wip rtfm_blinky rtfm_blinky marcus eq implemented and tested eq implemented and tested bare10 bare updates bare 6 assignment cargo fix various fixes
Per Lindgren authoredbt broken? Cargo.toml no nightly dep more examples preLaunchTask crash re-added more preLaunchTasks example/device and SVD openocd.gdb comment fixed and ext itm launch config added bare0 bare1 bare1 bare2 and bare3 bare2 and bare3 bare2 and bare3 bare4 and bare5 bare6 wip bare6 wip serial wip serial wip bare7 bare8 bare8 to fresk to fresk to fresk to fresk bare8, 9 to fresk bare7 polishing bare9 rtfm_blinky wip rtfm_blinky rtfm_blinky marcus eq implemented and tested eq implemented and tested bare10 bare updates bare 6 assignment cargo fix various fixes
operator.rs 19.30 KiB
use rustc::mir;
use rustc::ty::{Ty};
use rustc_const_math::ConstFloat;
use syntax::ast::FloatTy;
use std::cmp::Ordering;
use error::{EvalError, EvalResult};
use eval_context::{EvalContext, ValTy};
use lvalue::Lvalue;
use memory::{MemoryPointer, PointerOffset, SByte};
use value::{
PrimVal,
PrimValKind,
Value,
bytes_to_f32,
bytes_to_f64,
f32_to_bytes,
f64_to_bytes,
bytes_to_bool,
};
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
fn binop_with_overflow(
&mut self,
op: mir::BinOp,
left: &mir::Operand<'tcx>,
right: &mir::Operand<'tcx>,
) -> EvalResult<'tcx, (PrimVal, bool)> {
let left_ty = self.operand_ty(left);
let right_ty = self.operand_ty(right);
let left_val = self.eval_operand_to_primval(left)?;
let right_val = self.eval_operand_to_primval(right)?;
self.binary_op(op, left_val, left_ty, right_val, right_ty)
}
/// Applies the binary operation `op` to the two operands and writes a tuple of the result
/// and a boolean signifying the potential overflow to the destination.
pub(super) fn intrinsic_with_overflow(
&mut self,
op: mir::BinOp,
left: &mir::Operand<'tcx>,
right: &mir::Operand<'tcx>,
dest: Lvalue<'tcx>,
dest_ty: Ty<'tcx>,
) -> EvalResult<'tcx> {
let (val, overflowed) = self.binop_with_overflow(op, left, right)?;
let val = Value::ByValPair(val, PrimVal::from_bool(overflowed));
self.write_value(ValTy { value: val, ty: dest_ty}, dest)
}
/// Applies the binary operation `op` to the arguments and writes the result to the
/// destination. Returns `true` if the operation overflowed.
pub(super) fn intrinsic_overflowing(
&mut self,
op: mir::BinOp,
left: &mir::Operand<'tcx>,
right: &mir::Operand<'tcx>,
dest: Lvalue<'tcx>,
dest_ty: Ty<'tcx>,
) -> EvalResult<'tcx, bool> {
let (val, overflowed) = self.binop_with_overflow(op, left, right)?;
self.write_primval(dest, val, dest_ty)?;
Ok(overflowed)
}
}
macro_rules! overflow {
($op:ident, $l:expr, $r:expr) => ({
let (val, overflowed) = $l.$op($r);
let primval = PrimVal::Bytes(val as u128);
Ok((primval, overflowed))
})
}
macro_rules! int_arithmetic {
($kind:expr, $int_op:ident, $l:expr, $r:expr) => ({
let l = $l;
let r = $r;
match $kind {
I8 => overflow!($int_op, l as i8, r as i8),
I16 => overflow!($int_op, l as i16, r as i16),
I32 => overflow!($int_op, l as i32, r as i32),
I64 => overflow!($int_op, l as i64, r as i64),
I128 => overflow!($int_op, l as i128, r as i128),
U8 => overflow!($int_op, l as u8, r as u8),
U16 => overflow!($int_op, l as u16, r as u16),
U32 => overflow!($int_op, l as u32, r as u32),
U64 => overflow!($int_op, l as u64, r as u64),
U128 => overflow!($int_op, l as u128, r as u128),
_ => bug!("int_arithmetic should only be called on int primvals"),
}
})
}
macro_rules! int_shift {
($kind:expr, $int_op:ident, $l:expr, $r:expr) => ({
let l = $l;
let r = $r;
match $kind {
I8 => overflow!($int_op, l as i8, r),
I16 => overflow!($int_op, l as i16, r),
I32 => overflow!($int_op, l as i32, r),
I64 => overflow!($int_op, l as i64, r),
I128 => overflow!($int_op, l as i128, r),
U8 => overflow!($int_op, l as u8, r),
U16 => overflow!($int_op, l as u16, r),
U32 => overflow!($int_op, l as u32, r),
U64 => overflow!($int_op, l as u64, r),
U128 => overflow!($int_op, l as u128, r),
_ => bug!("int_shift should only be called on int primvals"),
}
})
}
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
/// Returns the result of the specified operation and whether it overflowed.
pub fn binary_op(
&mut self,
bin_op: mir::BinOp,
left: PrimVal,
left_ty: Ty<'tcx>,
right: PrimVal,
right_ty: Ty<'tcx>,
) -> EvalResult<'tcx, (PrimVal, bool)> {
use rustc::mir::BinOp::*;
use value::PrimValKind::*;
let left_kind = self.ty_to_primval_kind(left_ty)?;
let right_kind = self.ty_to_primval_kind(right_ty)?;
// I: Handle operations that support pointers
let usize = PrimValKind::from_uint_size(self.memory.pointer_size());
let isize = PrimValKind::from_int_size(self.memory.pointer_size());
if !left.is_concrete() || !right.is_concrete() {
return self.abstract_binary_op(bin_op,
left, left_kind,
right, right_kind);
}
match (left, right) {
(PrimVal::Ptr(lptr), PrimVal::Ptr(rptr)) => {
if !lptr.has_concrete_offset() || !rptr.has_concrete_offset() {
return self.abstract_ptr_ops(bin_op, lptr, left_kind, rptr, right_kind);
}
}
_ => {
}
}
if !left_kind.is_float() && !right_kind.is_float() {
match bin_op {
Offset if left_kind == Ptr && right_kind == usize => {
let pointee_ty = left_ty.builtin_deref(true).expect("Offset called on non-ptr type").ty;
let ptr = self.pointer_offset(left, pointee_ty, right.to_bytes()? as i64)?;
return Ok((ptr, false));
},
// These work on anything
Eq if left_kind == right_kind => {
let result = match (left, right) {
(PrimVal::Bytes(left), PrimVal::Bytes(right)) => left == right,
(PrimVal::Ptr(left), PrimVal::Ptr(right)) => left == right,
(PrimVal::Undef, _) | (_, PrimVal::Undef) => return Err(EvalError::ReadUndefBytes),
_ => false,
};
return Ok((PrimVal::from_bool(result), false));
}
Ne if left_kind == right_kind => {
let result = match (left, right) {
(PrimVal::Bytes(left), PrimVal::Bytes(right)) => left != right,
(PrimVal::Ptr(left), PrimVal::Ptr(right)) => left != right,
(PrimVal::Undef, _) | (_, PrimVal::Undef) => return Err(EvalError::ReadUndefBytes),
_ => true,
};
return Ok((PrimVal::from_bool(result), false));
}
// These need both pointers to be in the same allocation
Lt | Le | Gt | Ge | Sub
if left_kind == right_kind
&& (left_kind == Ptr || left_kind == usize || left_kind == isize)
&& left.is_ptr() && right.is_ptr() => {
let left = left.to_ptr()?;
let right = right.to_ptr()?;
let (left_offset, right_offset) = match (left.offset, right.offset) {
(PointerOffset::Concrete(l), PointerOffset::Concrete(r)) => (l, r),
_ => unimplemented!(),
};
if left.alloc_id == right.alloc_id {
let res = match bin_op {
Lt => left_offset < right_offset,
Le => left_offset <= right_offset,
Gt => left_offset > right_offset,
Ge => left_offset >= right_offset,
Sub => {
return int_arithmetic!(left_kind, overflowing_sub, left_offset, right_offset);
}
_ => bug!("We already established it has to be one of these operators."),
};
return Ok((PrimVal::from_bool(res), false));
} else {
// Both are pointers, but from different allocations.
return Err(EvalError::InvalidPointerMath);
}
}
// These work if one operand is a pointer, the other an integer
Add | BitAnd | Sub
if left_kind == right_kind && (left_kind == usize || left_kind == isize)
&& left.is_ptr() && right.is_bytes() => {
// Cast to i128 is fine as we checked the kind to be ptr-sized
return self.ptr_int_arithmetic(bin_op, left.to_ptr()?, right.to_bytes()? as i128, left_kind == isize);
}
Add | BitAnd
if left_kind == right_kind && (left_kind == usize || left_kind == isize)
&& left.is_bytes() && right.is_ptr() => {
// This is a commutative operation, just swap the operands
return self.ptr_int_arithmetic(bin_op, right.to_ptr()?, left.to_bytes()? as i128, left_kind == isize);
}
_ => {}
}
}
// II: From now on, everything must be bytes, no pointers
let l = left.to_bytes()?;
let r = right.to_bytes()?;
// These ops can have an RHS with a different numeric type.
if bin_op == Shl || bin_op == Shr {
return match bin_op {
Shl => int_shift!(left_kind, overflowing_shl, l, r as u32),
Shr => int_shift!(left_kind, overflowing_shr, l, r as u32),
_ => bug!("it has already been checked that this is a shift op"),
};
}
if left_kind != right_kind {
let msg = format!("unimplemented binary op: {:?}, {:?}, {:?}", left, right, bin_op);
return Err(EvalError::Unimplemented(msg));
}
let float_op = |op, l, r, ty| {
let l = ConstFloat {
bits: l,
ty,
};
let r = ConstFloat {
bits: r,
ty,
};
match op {
Eq => PrimVal::from_bool(l.try_cmp(r).unwrap() == Ordering::Equal),
Ne => PrimVal::from_bool(l.try_cmp(r).unwrap() != Ordering::Equal),
Lt => PrimVal::from_bool(l.try_cmp(r).unwrap() == Ordering::Less),
Le => PrimVal::from_bool(l.try_cmp(r).unwrap() != Ordering::Greater),
Gt => PrimVal::from_bool(l.try_cmp(r).unwrap() == Ordering::Greater),
Ge => PrimVal::from_bool(l.try_cmp(r).unwrap() != Ordering::Less),
Add => PrimVal::Bytes((l + r).unwrap().bits),
Sub => PrimVal::Bytes((l - r).unwrap().bits),
Mul => PrimVal::Bytes((l * r).unwrap().bits),
Div => PrimVal::Bytes((l / r).unwrap().bits),
Rem => PrimVal::Bytes((l % r).unwrap().bits),
_ => bug!("invalid float op: `{:?}`", op),
}
};
let val = match (bin_op, left_kind) {
(_, F32) => float_op(bin_op, l, r, FloatTy::F32),
(_, F64) => float_op(bin_op, l, r, FloatTy::F64),
(Eq, _) => PrimVal::from_bool(l == r),
(Ne, _) => PrimVal::from_bool(l != r),
(Lt, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) < (r as i128)),
(Lt, _) => PrimVal::from_bool(l < r),
(Le, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) <= (r as i128)),
(Le, _) => PrimVal::from_bool(l <= r),
(Gt, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) > (r as i128)),
(Gt, _) => PrimVal::from_bool(l > r),
(Ge, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) >= (r as i128)),
(Ge, _) => PrimVal::from_bool(l >= r),
(BitOr, _) => PrimVal::Bytes(l | r),
(BitAnd, _) => PrimVal::Bytes(l & r),
(BitXor, _) => PrimVal::Bytes(l ^ r),
(Add, k) if k.is_int() => return int_arithmetic!(k, overflowing_add, l, r),
(Sub, k) if k.is_int() => return int_arithmetic!(k, overflowing_sub, l, r),
(Mul, k) if k.is_int() => return int_arithmetic!(k, overflowing_mul, l, r),
(Div, k) if k.is_int() => return int_arithmetic!(k, overflowing_div, l, r),
(Rem, k) if k.is_int() => return int_arithmetic!(k, overflowing_rem, l, r),
_ => {
let msg = format!("unimplemented binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, left_kind, right, right_kind);
return Err(EvalError::Unimplemented(msg));
}
};
Ok((val, false))
}
fn ptr_int_arithmetic(
&self,
bin_op: mir::BinOp,
left: MemoryPointer,
right: i128,
signed: bool,
) -> EvalResult<'tcx, (PrimVal, bool)> {
use rustc::mir::BinOp::*;
fn map_to_primval((res, over) : (MemoryPointer, bool)) -> (PrimVal, bool) {
(PrimVal::Ptr(res), over)
}
let left_offset = match left.offset {
PointerOffset::Concrete(n) => n,
_ => unimplemented!(),
};
Ok(match bin_op {
Sub =>
// The only way this can overflow is by underflowing, so signdeness of the right operands does not matter
map_to_primval(left.overflowing_signed_offset(-right, self.memory.layout)),
Add if signed =>
map_to_primval(left.overflowing_signed_offset(right, self.memory.layout)),
Add if !signed =>
map_to_primval(left.overflowing_offset(right as u64, self.memory.layout)),
BitAnd if !signed => {
let base_mask : u64 = !(self.memory.get(left.alloc_id)?.align - 1);
let right = right as u64;
if right & base_mask == base_mask {
// Case 1: The base address bits are all preserved, i.e., right is all-1 there
(PrimVal::Ptr(MemoryPointer::new(left.alloc_id, left_offset & right)), false)
} else if right & base_mask == 0 {
// Case 2: The base address bits are all taken away, i.e., right is all-0 there
(PrimVal::from_u128((left_offset & right) as u128), false)
} else {
return Err(EvalError::ReadPointerAsBytes);
}
}
_ => {
let msg = format!("unimplemented binary op on pointer {:?}: {:?}, {:?} ({})", bin_op, left, right, if signed { "signed" } else { "unsigned" });
return Err(EvalError::Unimplemented(msg));
}
})
}
pub fn abstract_binary_op(
&mut self,
bin_op: mir::BinOp,
left: PrimVal,
left_kind: PrimValKind,
right: PrimVal,
mut right_kind: PrimValKind,
) -> EvalResult<'tcx, (PrimVal, bool)> {
// These ops can have an RHS with a different numeric type.
if bin_op == mir::BinOp::Shl || bin_op == mir::BinOp::Shr {
match (left, right) {
(PrimVal::Abstract(abytes), PrimVal::Bytes(rn)) if rn % 8 == 0 => {
let num_bytes = (rn / 8) as usize;
match bin_op {
mir::BinOp::Shl => {
let mut buffer = [SByte::Concrete(0); 8];
for idx in num_bytes .. 8 {
buffer[idx] = abytes[idx - num_bytes];
}
return Ok((PrimVal::Abstract(buffer), false));
}
mir::BinOp::Shr => {
if !left_kind.is_signed_int() {
let mut buffer = [SByte::Concrete(0); 8];
for idx in num_bytes .. 8 {
buffer[idx - num_bytes] = abytes[idx];
}
return Ok((PrimVal::Abstract(buffer), false));
}
}
_ => unimplemented!(),
}
}
_ => (),
}
if right_kind.num_bytes() == left_kind.num_bytes() {
right_kind = left_kind;
} else if right_kind.num_bytes() < left_kind.num_bytes() {
right_kind = left_kind;
} else {
if let PrimVal::Bytes(n) = right {
if n < 256 {
// HACK
right_kind = left_kind;
} else {
unimplemented!()
}
} else {
unimplemented!()
}
}
}
if left_kind != right_kind {
let msg = format!("unimplemented binary op: {:?}, {:?}, {:?}", left, right, bin_op);
return Err(EvalError::Unimplemented(msg));
}
Ok((self.memory.constraints.add_binop_constraint(bin_op, left, right, left_kind), false))
}
fn abstract_ptr_ops(
&mut self,
bin_op: mir::BinOp,
left: MemoryPointer,
_left_kind: PrimValKind,
right: MemoryPointer,
_right_kind: PrimValKind,
) -> EvalResult<'tcx, (PrimVal, bool)> {
use rustc::mir::BinOp::*;
use value::PrimValKind::*;
if left.alloc_id != right.alloc_id {
if let Eq = bin_op {
unimplemented!()
} else {
unimplemented!()
}
} else {
let result = self.memory.constraints.add_binop_constraint(
bin_op, left.offset.as_primval(), right.offset.as_primval(), U64);
Ok((result, false))
}
}
pub fn unary_op(
&mut self,
un_op: mir::UnOp,
val: PrimVal,
val_kind: PrimValKind,
) -> EvalResult<'tcx, PrimVal> {
use rustc::mir::UnOp::*;
use value::PrimValKind::*;
if !val.is_concrete() {
return
Ok(self.memory.constraints.add_unop_constraint(
un_op, val, val_kind))
}
let bytes = val.to_bytes()?;
let result_bytes = match (un_op, val_kind) {
(Not, Bool) => !bytes_to_bool(bytes) as u128,
(Not, U8) => !(bytes as u8) as u128,
(Not, U16) => !(bytes as u16) as u128,
(Not, U32) => !(bytes as u32) as u128,
(Not, U64) => !(bytes as u64) as u128,
(Not, U128) => !bytes,
(Not, I8) => !(bytes as i8) as u128,
(Not, I16) => !(bytes as i16) as u128,
(Not, I32) => !(bytes as i32) as u128,
(Not, I64) => !(bytes as i64) as u128,
(Not, I128) => !(bytes as i128) as u128,
(Neg, I8) => -(bytes as i8) as u128,
(Neg, I16) => -(bytes as i16) as u128,
(Neg, I32) => -(bytes as i32) as u128,
(Neg, I64) => -(bytes as i64) as u128,
(Neg, I128) => -(bytes as i128) as u128,
(Neg, F32) => f32_to_bytes(-bytes_to_f32(bytes)),
(Neg, F64) => f64_to_bytes(-bytes_to_f64(bytes)),
_ => {
let msg = format!("unimplemented unary op: {:?}, {:?}", un_op, val);
return Err(EvalError::Unimplemented(msg));
}
};
Ok(PrimVal::Bytes(result_bytes))
}
}