src_ty: Ty<'tcx>,
dest_ty: Ty<'tcx>,
) -> EvalResult<'tcx, PrimVal> {
+ use rustc::ty::TypeVariants::*;
trace!("Casting {:?}: {:?} to {:?}", val, src_ty, dest_ty);
- let src_kind = self.ty_to_primval_kind(src_ty)?;
match val {
PrimVal::Undef => Ok(PrimVal::Undef),
PrimVal::Ptr(ptr) => self.cast_from_ptr(ptr, dest_ty),
- val @ PrimVal::Bytes(_) => {
- use rustc::mir::interpret::PrimValKind::*;
- match src_kind {
- F32 => self.cast_from_float(val.to_f32()?, dest_ty),
- F64 => self.cast_from_float(val.to_f64()?, dest_ty),
-
- I8 | I16 | I32 | I64 | I128 => {
- self.cast_from_signed_int(val.to_i128()?, dest_ty)
- }
-
- Bool | Char | U8 | U16 | U32 | U64 | U128 | FnPtr | Ptr => {
- self.cast_from_int(val.to_u128()?, dest_ty, false)
- }
+ PrimVal::Bytes(b) => {
+ match src_ty.sty {
+ TyFloat(fty) => self.cast_from_float(b, fty, dest_ty),
+ _ => self.cast_from_int(b, src_ty, dest_ty),
}
}
}
}
- fn cast_from_signed_int(&self, val: i128, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
- self.cast_from_int(val as u128, ty, val < 0)
- }
-
- fn int_to_int(&self, v: i128, ty: IntTy) -> u128 {
- match ty {
- IntTy::I8 => v as i8 as u128,
- IntTy::I16 => v as i16 as u128,
- IntTy::I32 => v as i32 as u128,
- IntTy::I64 => v as i64 as u128,
- IntTy::I128 => v as u128,
- IntTy::Isize => {
- let ty = self.tcx.sess.target.isize_ty;
- self.int_to_int(v, ty)
- }
- }
- }
- fn int_to_uint(&self, v: u128, ty: UintTy) -> u128 {
- match ty {
- UintTy::U8 => v as u8 as u128,
- UintTy::U16 => v as u16 as u128,
- UintTy::U32 => v as u32 as u128,
- UintTy::U64 => v as u64 as u128,
- UintTy::U128 => v,
- UintTy::Usize => {
- let ty = self.tcx.sess.target.usize_ty;
- self.int_to_uint(v, ty)
- }
- }
- }
-
fn cast_from_int(
&self,
v: u128,
- ty: Ty<'tcx>,
- negative: bool,
+ src_ty: Ty<'tcx>,
+ dest_ty: Ty<'tcx>,
) -> EvalResult<'tcx, PrimVal> {
- trace!("cast_from_int: {}, {}, {}", v, ty, negative);
+ trace!("cast_from_int: {}, {}, {}", v, src_ty, dest_ty);
use rustc::ty::TypeVariants::*;
- match ty.sty {
- // Casts to bool are not permitted by rustc, no need to handle them here.
- TyInt(ty) => Ok(PrimVal::Bytes(self.int_to_int(v as i128, ty))),
- TyUint(ty) => Ok(PrimVal::Bytes(self.int_to_uint(v, ty))),
+ match dest_ty.sty {
+ TyInt(_) | TyUint(_) => {
+ let v = self.sign_extend(v, src_ty)?;
+ let v = self.truncate(v, dest_ty)?;
+ Ok(PrimVal::Bytes(v))
+ }
- TyFloat(fty) if negative => Ok(PrimVal::Bytes(ConstFloat::from_i128(v as i128, fty).bits)),
+ TyFloat(fty) if src_ty.is_signed() => Ok(PrimVal::Bytes(ConstFloat::from_i128(v as i128, fty).bits)),
TyFloat(fty) => Ok(PrimVal::Bytes(ConstFloat::from_u128(v, fty).bits)),
TyChar if v as u8 as u128 == v => Ok(PrimVal::Bytes(v)),
// No alignment check needed for raw pointers. But we have to truncate to target ptr size.
TyRawPtr(_) => Ok(PrimVal::Bytes(self.memory.truncate_to_ptr(v).0 as u128)),
- _ => err!(Unimplemented(format!("int to {:?} cast", ty))),
+ // Casts to bool are not permitted by rustc, no need to handle them here.
+ _ => err!(Unimplemented(format!("int to {:?} cast", dest_ty))),
}
}
- fn cast_from_float(&self, val: ConstFloat, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
+ fn cast_from_float(&self, bits: u128, fty: FloatTy, dest_ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
use rustc::ty::TypeVariants::*;
- match ty.sty {
+ use rustc_apfloat::FloatConvert;
+ match dest_ty.sty {
+ // float -> uint
TyUint(t) => {
let width = t.bit_width().unwrap_or(self.memory.pointer_size() as usize * 8);
- match val.ty {
- FloatTy::F32 => Ok(PrimVal::Bytes(Single::from_bits(val.bits).to_u128(width).value)),
- FloatTy::F64 => Ok(PrimVal::Bytes(Double::from_bits(val.bits).to_u128(width).value)),
+ match fty {
+ FloatTy::F32 => Ok(PrimVal::Bytes(Single::from_bits(bits).to_u128(width).value)),
+ FloatTy::F64 => Ok(PrimVal::Bytes(Double::from_bits(bits).to_u128(width).value)),
}
},
-
+ // float -> int
TyInt(t) => {
let width = t.bit_width().unwrap_or(self.memory.pointer_size() as usize * 8);
- match val.ty {
- FloatTy::F32 => Ok(PrimVal::from_i128(Single::from_bits(val.bits).to_i128(width).value)),
- FloatTy::F64 => Ok(PrimVal::from_i128(Double::from_bits(val.bits).to_i128(width).value)),
+ match fty {
+ FloatTy::F32 => Ok(PrimVal::from_i128(Single::from_bits(bits).to_i128(width).value)),
+ FloatTy::F64 => Ok(PrimVal::from_i128(Double::from_bits(bits).to_i128(width).value)),
}
},
-
- TyFloat(fty) => Ok(PrimVal::from_float(val.convert(fty))),
- _ => err!(Unimplemented(format!("float to {:?} cast", ty))),
+ // f64 -> f32
+ TyFloat(FloatTy::F32) if fty == FloatTy::F64 => {
+ Ok(PrimVal::Bytes(Single::to_bits(Double::from_bits(bits).convert(&mut false).value)))
+ },
+ // f32 -> f64
+ TyFloat(FloatTy::F64) if fty == FloatTy::F32 => {
+ Ok(PrimVal::Bytes(Double::to_bits(Single::from_bits(bits).convert(&mut false).value)))
+ },
+ // identity cast
+ TyFloat(_) => Ok(PrimVal::Bytes(bits)),
+ _ => err!(Unimplemented(format!("float to {:?} cast", dest_ty))),
}
}
use rustc::mir;
-use rustc::ty::Ty;
+use rustc::ty::{self, Ty};
use rustc_const_math::ConstFloat;
use syntax::ast::FloatTy;
use std::cmp::Ordering;
+use rustc::ty::layout::LayoutOf;
use super::{EvalContext, Place, Machine, ValTy};
-use rustc::mir::interpret::{EvalResult, PrimVal, PrimValKind, Value, bytes_to_f32, bytes_to_f64};
+use rustc::mir::interpret::{EvalResult, PrimVal, Value};
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
fn binop_with_overflow(
}
}
-macro_rules! overflow {
- (overflowing_div, $l:expr, $r:expr) => ({
- let (val, overflowed) = if $r == 0 {
- ($l, true)
- } else {
- $l.overflowing_div($r)
- };
- let primval = PrimVal::Bytes(val as u128);
- Ok((primval, overflowed))
- });
- (overflowing_rem, $l:expr, $r:expr) => ({
- let (val, overflowed) = if $r == 0 {
- ($l, true)
- } else {
- $l.overflowing_rem($r)
- };
- let primval = PrimVal::Bytes(val as u128);
- Ok((primval, overflowed))
- });
- ($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;
- use rustc::mir::interpret::PrimValKind::*;
- 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;
- let r_wrapped = r as u32;
- match $kind {
- I8 => overflow!($int_op, l as i8, r_wrapped),
- I16 => overflow!($int_op, l as i16, r_wrapped),
- I32 => overflow!($int_op, l as i32, r_wrapped),
- I64 => overflow!($int_op, l as i64, r_wrapped),
- I128 => overflow!($int_op, l as i128, r_wrapped),
- U8 => overflow!($int_op, l as u8, r_wrapped),
- U16 => overflow!($int_op, l as u16, r_wrapped),
- U32 => overflow!($int_op, l as u32, r_wrapped),
- U64 => overflow!($int_op, l as u64, r_wrapped),
- U128 => overflow!($int_op, l as u128, r_wrapped),
- _ => bug!("int_shift should only be called on int primvals"),
- }.map(|(val, over)| (val, over || r != r_wrapped as u128))
- })
-}
-
impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> {
/// Returns the result of the specified operation and whether it overflowed.
pub fn binary_op(
right_ty: Ty<'tcx>,
) -> EvalResult<'tcx, (PrimVal, bool)> {
use rustc::mir::BinOp::*;
- use rustc::mir::interpret::PrimValKind::*;
let left_kind = self.ty_to_primval_kind(left_ty)?;
let right_kind = self.ty_to_primval_kind(right_ty)?;
- //trace!("Running binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, left_kind, right, right_kind);
+ trace!("Running binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, left_kind, right, right_kind);
// I: Handle operations that support pointers
if !left_kind.is_float() && !right_kind.is_float() {
// These ops can have an RHS with a different numeric type.
if right_kind.is_int() && (bin_op == Shl || bin_op == Shr) {
- return match bin_op {
- Shl => int_shift!(left_kind, overflowing_shl, l, r),
- Shr => int_shift!(left_kind, overflowing_shr, l, r),
+ let op: fn(u128, u32) -> (u128, bool) = match bin_op {
+ Shl => u128::overflowing_shl,
+ Shr => u128::overflowing_shr,
_ => bug!("it has already been checked that this is a shift op"),
};
+ let l = if left_ty.is_signed() {
+ self.sign_extend(l, left_ty)?
+ } else {
+ l
+ };
+ let (result, oflo) = op(l, r as u32);
+ let truncated = self.truncate(result, left_ty)?;
+ return Ok((PrimVal::Bytes(truncated), oflo || truncated != result));
}
if left_kind != right_kind {
}
};
- 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),
+ if left_ty.is_signed() {
+ let op: Option<fn(&i128, &i128) -> bool> = match bin_op {
+ Lt => Some(i128::lt),
+ Le => Some(i128::le),
+ Gt => Some(i128::gt),
+ Ge => Some(i128::ge),
+ _ => None,
+ };
+ if let Some(op) = op {
+ let l = self.sign_extend(l, left_ty)? as i128;
+ let r = self.sign_extend(r, right_ty)? as i128;
+ return Ok((PrimVal::from_bool(op(&l, &r)), false));
+ }
+ let op: Option<fn(i128, i128) -> (i128, bool)> = match bin_op {
+ Rem | Div if r == 0 => return Ok((PrimVal::Bytes(l), true)),
+ Div => Some(i128::overflowing_div),
+ Rem => Some(i128::overflowing_rem),
+ Add => Some(i128::overflowing_add),
+ Sub => Some(i128::overflowing_sub),
+ Mul => Some(i128::overflowing_mul),
+ _ => None,
+ };
+ if let Some(op) = op {
+ let l128 = self.sign_extend(l, left_ty)? as i128;
+ let r = self.sign_extend(r, right_ty)? as i128;
+ let size = self.layout_of(left_ty)?.size.bits();
+ match bin_op {
+ Rem | Div => {
+ // int_min / -1
+ if r == -1 && l == (1 << (size - 1)) {
+ return Ok((PrimVal::Bytes(l), true));
+ }
+ },
+ _ => {},
+ }
+ trace!("{}, {}, {}", l, l128, r);
+ let (result, mut oflo) = op(l128, r);
+ trace!("{}, {}", result, oflo);
+ if !oflo && size != 128 {
+ let max = 1 << (size - 1);
+ oflo = result >= max || result < -max;
+ }
+ let result = result as u128;
+ let truncated = self.truncate(result, left_ty)?;
+ return Ok((PrimVal::Bytes(truncated), oflo));
+ }
+ }
- (BitOr, _) => PrimVal::Bytes(l | r),
- (BitAnd, _) => PrimVal::Bytes(l & r),
- (BitXor, _) => PrimVal::Bytes(l ^ r),
+ if let ty::TyFloat(fty) = left_ty.sty {
+ return Ok((float_op(bin_op, l, r, fty), false));
+ }
- (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),
+ // only ints left
+ let val = match bin_op {
+ Eq => PrimVal::from_bool(l == r),
+ Ne => PrimVal::from_bool(l != r),
+
+ Lt => PrimVal::from_bool(l < r),
+ Le => PrimVal::from_bool(l <= r),
+ Gt => PrimVal::from_bool(l > r),
+ Ge => PrimVal::from_bool(l >= r),
+
+ BitOr => PrimVal::Bytes(l | r),
+ BitAnd => PrimVal::Bytes(l & r),
+ BitXor => PrimVal::Bytes(l ^ r),
+
+ Add | Sub | Mul | Rem | Div => {
+ let op: fn(u128, u128) -> (u128, bool) = match bin_op {
+ Add => u128::overflowing_add,
+ Sub => u128::overflowing_sub,
+ Mul => u128::overflowing_mul,
+ Rem | Div if r == 0 => return Ok((PrimVal::Bytes(l), true)),
+ Div => u128::overflowing_div,
+ Rem => u128::overflowing_rem,
+ _ => bug!(),
+ };
+ let (result, oflo) = op(l, r);
+ let truncated = self.truncate(result, left_ty)?;
+ return Ok((PrimVal::Bytes(truncated), oflo || truncated != result));
+ }
_ => {
let msg = format!(
"unimplemented binary op {:?}: {:?} ({:?}), {:?} ({:?})",
bin_op,
left,
- left_kind,
+ left_ty,
right,
- right_kind
+ right_ty,
);
return err!(Unimplemented(msg));
}
Ok((val, false))
}
-}
-pub fn unary_op<'tcx>(
- un_op: mir::UnOp,
- val: PrimVal,
- val_kind: PrimValKind,
-) -> EvalResult<'tcx, PrimVal> {
- use rustc::mir::UnOp::*;
- use rustc::mir::interpret::PrimValKind::*;
-
- let bytes = val.to_bytes()?;
+ pub fn unary_op(
+ &self,
+ un_op: mir::UnOp,
+ val: PrimVal,
+ ty: Ty<'tcx>,
+ ) -> EvalResult<'tcx, PrimVal> {
+ use rustc::mir::UnOp::*;
+ use rustc_apfloat::ieee::{Single, Double};
+ use rustc_apfloat::Float;
- let result_bytes = match (un_op, val_kind) {
- (Not, Bool) => !val.to_bool()? as u128,
+ let bytes = val.to_bytes()?;
+ let size = self.layout_of(ty)?.size.bits();
- (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,
+ let result_bytes = match (un_op, &ty.sty) {
- (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,
+ (Not, ty::TyBool) => !val.to_bool()? as u128,
- (Neg, I8) if bytes == i8::min_value() as u128 => return err!(OverflowingMath),
- (Neg, I8) => -(bytes as i8) as u128,
- (Neg, I16) if bytes == i16::min_value() as u128 => return err!(OverflowingMath),
- (Neg, I16) => -(bytes as i16) as u128,
- (Neg, I32) if bytes == i32::min_value() as u128 => return err!(OverflowingMath),
- (Neg, I32) => -(bytes as i32) as u128,
- (Neg, I64) if bytes == i64::min_value() as u128 => return err!(OverflowingMath),
- (Neg, I64) => -(bytes as i64) as u128,
- (Neg, I128) if bytes == i128::min_value() as u128 => return err!(OverflowingMath),
- (Neg, I128) => -(bytes as i128) as u128,
+ (Not, _) => !bytes,
- (Neg, F32) => (-bytes_to_f32(bytes)).bits,
- (Neg, F64) => (-bytes_to_f64(bytes)).bits,
+ (Neg, ty::TyFloat(FloatTy::F32)) => Single::to_bits(-Single::from_bits(bytes)),
+ (Neg, ty::TyFloat(FloatTy::F64)) => Double::to_bits(-Double::from_bits(bytes)),
- _ => {
- let msg = format!("unimplemented unary op: {:?}, {:?}", un_op, val);
- return err!(Unimplemented(msg));
- }
- };
+ (Neg, _) if bytes == (1 << (size - 1)) => return err!(OverflowingMath),
+ (Neg, _) => (-(bytes as i128)) as u128,
+ };
- Ok(PrimVal::Bytes(result_bytes))
+ Ok(PrimVal::Bytes(self.truncate(result_bytes, ty)?))
+ }
}